xref: /reactos/win32ss/user/ntuser/caret.c (revision 09dde2cf)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS Win32k subsystem
4  * PURPOSE:          Caret functions
5  * FILE:             win32ss/user/ntuser/caret.c
6  * PROGRAMERS:       Thomas Weidenmueller (w3seek@users.sourceforge.net)
7  *                   Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
8  */
9 
10 #include <win32k.h>
11 DBG_DEFAULT_CHANNEL(UserCaret);
12 
13 /* FUNCTIONS *****************************************************************/
14 
15 VOID FASTCALL
16 co_IntDrawCaret(PWND pWnd, PTHRDCARETINFO CaretInfo)
17 {
18     HDC hdc, hdcMem;
19     HBITMAP hbmOld;
20     RECT rcClient;
21     BOOL bDone = FALSE;
22 
23     if (pWnd == NULL)
24     {
25        TRACE("Null Window!\n");
26        return;
27     }
28 
29     hdc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE);
30     if (!hdc)
31     {
32         ERR("GetDC failed\n");
33         return;
34     }
35 
36     if (pWnd->hrgnUpdate)
37     {
38        NtGdiSaveDC(hdc);
39     }
40 
41     IntGetClientRect(pWnd, &rcClient);
42     NtGdiIntersectClipRect(hdc,
43                            rcClient.left,
44                            rcClient.top,
45                            rcClient.right,
46                            rcClient.bottom);
47 
48     if (CaretInfo->Bitmap)
49     {
50         if (!GreGetBitmapDimension(CaretInfo->Bitmap, &CaretInfo->Size))
51         {
52             ERR("Failed to get bitmap dimensions\n");
53             goto cleanup;
54         }
55 
56         hdcMem = NtGdiCreateCompatibleDC(hdc);
57         if (hdcMem)
58         {
59             hbmOld = NtGdiSelectBitmap(hdcMem, CaretInfo->Bitmap);
60             bDone = NtGdiBitBlt(hdc,
61                                 CaretInfo->Pos.x,
62                                 CaretInfo->Pos.y,
63                                 CaretInfo->Size.cx,
64                                 CaretInfo->Size.cy,
65                                 hdcMem,
66                                 0,
67                                 0,
68                                 SRCINVERT,
69                                 0,
70                                 0);
71             NtGdiSelectBitmap(hdcMem, hbmOld);
72             GreDeleteObject(hdcMem);
73         }
74     }
75 
76     if (!bDone)
77     {
78         NtGdiPatBlt(hdc,
79                     CaretInfo->Pos.x,
80                     CaretInfo->Pos.y,
81                     CaretInfo->Size.cx,
82                     CaretInfo->Size.cy,
83                     DSTINVERT);
84     }
85 
86 cleanup:
87     if (pWnd->hrgnUpdate)
88     {
89        NtGdiRestoreDC(hdc, -1);
90     }
91 
92     UserReleaseDC(pWnd, hdc, FALSE);
93 }
94 
95 VOID
96 CALLBACK
97 CaretSystemTimerProc(HWND hwnd,
98                      UINT uMsg,
99                      UINT_PTR idEvent,
100                      DWORD dwTime)
101 {
102    PTHREADINFO pti;
103    PUSER_MESSAGE_QUEUE ThreadQueue;
104    PWND pWnd;
105 
106    pti = PsGetCurrentThreadWin32Thread();
107    ThreadQueue = pti->MessageQueue;
108 
109    if (ThreadQueue->CaretInfo.hWnd != hwnd)
110    {
111       TRACE("Not the same caret window!\n");
112       return;
113    }
114 
115    if (hwnd)
116    {
117       pWnd = UserGetWindowObject(hwnd);
118       if (!pWnd)
119       {
120          ERR("Caret System Timer Proc has invalid window handle! %p Id: %u\n", hwnd, idEvent);
121          return;
122       }
123    }
124    else
125    {
126       TRACE( "Windowless Caret Timer Running!\n" );
127       return;
128    }
129 
130    switch (idEvent)
131    {
132       case IDCARETTIMER:
133       {
134          ThreadQueue->CaretInfo.Showing = (ThreadQueue->CaretInfo.Showing ? 0 : 1);
135          co_IntDrawCaret(pWnd, &ThreadQueue->CaretInfo);
136       }
137    }
138    return;
139 }
140 
141 static
142 BOOL FASTCALL
143 co_IntHideCaret(PTHRDCARETINFO CaretInfo)
144 {
145    PWND pWnd;
146    if(CaretInfo->hWnd && CaretInfo->Visible && CaretInfo->Showing)
147    {
148       pWnd = UserGetWindowObject(CaretInfo->hWnd);
149       CaretInfo->Showing = 0;
150 
151       co_IntDrawCaret(pWnd, CaretInfo);
152       IntNotifyWinEvent(EVENT_OBJECT_HIDE, pWnd, OBJID_CARET, CHILDID_SELF, 0);
153       return TRUE;
154    }
155    return FALSE;
156 }
157 
158 BOOL FASTCALL
159 co_IntDestroyCaret(PTHREADINFO Win32Thread)
160 {
161    PUSER_MESSAGE_QUEUE ThreadQueue;
162    PWND pWnd;
163    ThreadQueue = Win32Thread->MessageQueue;
164 
165    if (!ThreadQueue)
166       return FALSE;
167 
168    pWnd = ValidateHwndNoErr(ThreadQueue->CaretInfo.hWnd);
169    co_IntHideCaret(&ThreadQueue->CaretInfo);
170    ThreadQueue->CaretInfo.Bitmap = (HBITMAP)0;
171    ThreadQueue->CaretInfo.hWnd = (HWND)0;
172    ThreadQueue->CaretInfo.Size.cx = ThreadQueue->CaretInfo.Size.cy = 0;
173    ThreadQueue->CaretInfo.Showing = 0;
174    ThreadQueue->CaretInfo.Visible = 0;
175    if (pWnd)
176    {
177       IntNotifyWinEvent(EVENT_OBJECT_DESTROY, pWnd, OBJID_CARET, CHILDID_SELF, 0);
178    }
179    return TRUE;
180 }
181 
182 BOOL FASTCALL
183 IntSetCaretBlinkTime(UINT uMSeconds)
184 {
185    /* Don't save the new value to the registry! */
186 
187    gpsi->dtCaretBlink = uMSeconds;
188 
189    return TRUE;
190 }
191 
192 BOOL FASTCALL
193 co_IntSetCaretPos(int X, int Y)
194 {
195    PTHREADINFO pti;
196    PWND pWnd;
197    PUSER_MESSAGE_QUEUE ThreadQueue;
198 
199    pti = PsGetCurrentThreadWin32Thread();
200    ThreadQueue = pti->MessageQueue;
201 
202    if(ThreadQueue->CaretInfo.hWnd)
203    {
204       pWnd = UserGetWindowObject(ThreadQueue->CaretInfo.hWnd);
205       if(ThreadQueue->CaretInfo.Pos.x != X || ThreadQueue->CaretInfo.Pos.y != Y)
206       {
207          co_IntHideCaret(&ThreadQueue->CaretInfo);
208          ThreadQueue->CaretInfo.Pos.x = X;
209          ThreadQueue->CaretInfo.Pos.y = Y;
210          if (ThreadQueue->CaretInfo.Visible)
211          {
212             ThreadQueue->CaretInfo.Showing = 1;
213             co_IntDrawCaret(pWnd, &ThreadQueue->CaretInfo);
214          }
215 
216          IntSetTimer(pWnd, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM);
217          IntNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, pWnd, OBJID_CARET, CHILDID_SELF, 0);
218       }
219       return TRUE;
220    }
221 
222    return FALSE;
223 }
224 
225 BOOL FASTCALL co_UserHideCaret(PWND Window OPTIONAL)
226 {
227    PTHREADINFO pti;
228    PUSER_MESSAGE_QUEUE ThreadQueue;
229 
230    if (Window) ASSERT_REFS_CO(Window);
231 
232    if(Window && Window->head.pti->pEThread != PsGetCurrentThread())
233    {
234       EngSetLastError(ERROR_ACCESS_DENIED);
235       return FALSE;
236    }
237 
238    pti = PsGetCurrentThreadWin32Thread();
239    ThreadQueue = pti->MessageQueue;
240 
241    if(Window && ThreadQueue->CaretInfo.hWnd != Window->head.h)
242    {
243       EngSetLastError(ERROR_ACCESS_DENIED);
244       return FALSE;
245    }
246 
247    if(ThreadQueue->CaretInfo.Visible)
248    {
249       PWND pwnd = UserGetWindowObject(ThreadQueue->CaretInfo.hWnd);
250       IntKillTimer(pwnd, IDCARETTIMER, TRUE);
251 
252       co_IntHideCaret(&ThreadQueue->CaretInfo);
253       ThreadQueue->CaretInfo.Visible = 0;
254       ThreadQueue->CaretInfo.Showing = 0;
255    }
256 
257    return TRUE;
258 }
259 
260 BOOL FASTCALL co_UserShowCaret(PWND Window OPTIONAL)
261 {
262    PTHREADINFO pti;
263    PUSER_MESSAGE_QUEUE ThreadQueue;
264    PWND pWnd = NULL;
265 
266    if (Window) ASSERT_REFS_CO(Window);
267 
268    if(Window && Window->head.pti->pEThread != PsGetCurrentThread())
269    {
270       EngSetLastError(ERROR_ACCESS_DENIED);
271       return FALSE;
272    }
273 
274    pti = PsGetCurrentThreadWin32Thread();
275    ThreadQueue = pti->MessageQueue;
276 
277    if(Window && ThreadQueue->CaretInfo.hWnd != Window->head.h)
278    {
279       EngSetLastError(ERROR_ACCESS_DENIED);
280       return FALSE;
281    }
282 
283    if (!ThreadQueue->CaretInfo.Visible)
284    {
285       ThreadQueue->CaretInfo.Visible = 1;
286       pWnd = ValidateHwndNoErr(ThreadQueue->CaretInfo.hWnd);
287       if (!ThreadQueue->CaretInfo.Showing && pWnd)
288       {
289          IntNotifyWinEvent(EVENT_OBJECT_SHOW, pWnd, OBJID_CARET, OBJID_CARET, 0);
290       }
291       if ((INT)gpsi->dtCaretBlink > 0)
292       {
293           IntSetTimer(pWnd, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM);
294       }
295       else if (ThreadQueue->CaretInfo.Visible)
296       {
297           ThreadQueue->CaretInfo.Showing = 1;
298           co_IntDrawCaret(pWnd, &ThreadQueue->CaretInfo);
299       }
300    }
301    return TRUE;
302 }
303 
304 /* SYSCALLS *****************************************************************/
305 
306 BOOL
307 APIENTRY
308 NtUserCreateCaret(
309    HWND hWnd,
310    HBITMAP hBitmap,
311    int nWidth,
312    int nHeight)
313 {
314    PWND Window;
315    PTHREADINFO pti;
316    PUSER_MESSAGE_QUEUE ThreadQueue;
317    DECLARE_RETURN(BOOL);
318 
319    TRACE("Enter NtUserCreateCaret\n");
320    UserEnterExclusive();
321 
322    if(!(Window = UserGetWindowObject(hWnd)))
323    {
324       RETURN(FALSE);
325    }
326 
327    if(Window->head.pti->pEThread != PsGetCurrentThread())
328    {
329       EngSetLastError(ERROR_ACCESS_DENIED);
330       RETURN(FALSE);
331    }
332 
333    pti = PsGetCurrentThreadWin32Thread();
334    ThreadQueue = pti->MessageQueue;
335 
336    if (ThreadQueue->CaretInfo.Visible)
337    {
338       IntKillTimer(Window, IDCARETTIMER, TRUE);
339       co_IntHideCaret(&ThreadQueue->CaretInfo);
340    }
341 
342    ThreadQueue->CaretInfo.hWnd = hWnd;
343    if(hBitmap)
344    {
345       ThreadQueue->CaretInfo.Bitmap = hBitmap;
346       ThreadQueue->CaretInfo.Size.cx = ThreadQueue->CaretInfo.Size.cy = 0;
347    }
348    else
349    {
350       if (nWidth == 0)
351       {
352           nWidth = UserGetSystemMetrics(SM_CXBORDER);
353       }
354       if (nHeight == 0)
355       {
356           nHeight = UserGetSystemMetrics(SM_CYBORDER);
357       }
358       ThreadQueue->CaretInfo.Bitmap = (HBITMAP)0;
359       ThreadQueue->CaretInfo.Size.cx = nWidth;
360       ThreadQueue->CaretInfo.Size.cy = nHeight;
361    }
362    ThreadQueue->CaretInfo.Visible = 0;
363    ThreadQueue->CaretInfo.Showing = 0;
364 
365    IntSetTimer(Window, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM);
366 
367    IntNotifyWinEvent(EVENT_OBJECT_CREATE, Window, OBJID_CARET, CHILDID_SELF, 0);
368 
369    RETURN(TRUE);
370 
371 CLEANUP:
372    TRACE("Leave NtUserCreateCaret, ret=%i\n",_ret_);
373    UserLeave();
374    END_CLEANUP;
375 }
376 
377 UINT
378 APIENTRY
379 NtUserGetCaretBlinkTime(VOID)
380 {
381    UINT ret;
382 
383    UserEnterShared();
384 
385    ret = gpsi->dtCaretBlink;
386 
387    UserLeave();
388 
389    return ret;
390 }
391 
392 BOOL
393 APIENTRY
394 NtUserGetCaretPos(
395    LPPOINT lpPoint)
396 {
397    PTHREADINFO pti;
398    PUSER_MESSAGE_QUEUE ThreadQueue;
399    NTSTATUS Status;
400    DECLARE_RETURN(BOOL);
401 
402    TRACE("Enter NtUserGetCaretPos\n");
403    UserEnterShared();
404 
405    pti = PsGetCurrentThreadWin32Thread();
406    ThreadQueue = pti->MessageQueue;
407 
408    Status = MmCopyToCaller(lpPoint, &ThreadQueue->CaretInfo.Pos, sizeof(POINT));
409    if(!NT_SUCCESS(Status))
410    {
411       SetLastNtError(Status);
412       RETURN(FALSE);
413    }
414 
415    RETURN(TRUE);
416 
417 CLEANUP:
418    TRACE("Leave NtUserGetCaretPos, ret=%i\n",_ret_);
419    UserLeave();
420    END_CLEANUP;
421 }
422 
423 BOOL
424 APIENTRY
425 NtUserShowCaret(HWND hWnd OPTIONAL)
426 {
427    PWND Window = NULL;
428    USER_REFERENCE_ENTRY Ref;
429    DECLARE_RETURN(BOOL);
430    BOOL ret;
431 
432    TRACE("Enter NtUserShowCaret\n");
433    UserEnterExclusive();
434 
435    if(hWnd && !(Window = UserGetWindowObject(hWnd)))
436    {
437       RETURN(FALSE);
438    }
439 
440    if (Window) UserRefObjectCo(Window, &Ref);
441 
442    ret = co_UserShowCaret(Window);
443 
444    if (Window) UserDerefObjectCo(Window);
445 
446    RETURN(ret);
447 
448 CLEANUP:
449    TRACE("Leave NtUserShowCaret, ret=%i\n",_ret_);
450    UserLeave();
451    END_CLEANUP;
452 }
453 
454 BOOL
455 APIENTRY
456 NtUserHideCaret(HWND hWnd OPTIONAL)
457 {
458    PWND Window = NULL;
459    USER_REFERENCE_ENTRY Ref;
460    DECLARE_RETURN(BOOL);
461    BOOL ret;
462 
463    TRACE("Enter NtUserHideCaret\n");
464    UserEnterExclusive();
465 
466    if(hWnd && !(Window = UserGetWindowObject(hWnd)))
467    {
468       RETURN(FALSE);
469    }
470 
471    if (Window) UserRefObjectCo(Window, &Ref);
472 
473    ret = co_UserHideCaret(Window);
474 
475    if (Window) UserDerefObjectCo(Window);
476 
477    RETURN(ret);
478 
479 CLEANUP:
480    TRACE("Leave NtUserHideCaret, ret=%i\n",_ret_);
481    UserLeave();
482    END_CLEANUP;
483 }
484