xref: /reactos/win32ss/user/ntuser/caret.c (revision 9cfd8dd9)
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 /* Win: zzzHideCaret */
226 BOOL FASTCALL co_UserHideCaret(PWND Window OPTIONAL)
227 {
228    PTHREADINFO pti;
229    PUSER_MESSAGE_QUEUE ThreadQueue;
230 
231    if (Window) ASSERT_REFS_CO(Window);
232 
233    if(Window && Window->head.pti->pEThread != PsGetCurrentThread())
234    {
235       EngSetLastError(ERROR_ACCESS_DENIED);
236       return FALSE;
237    }
238 
239    pti = PsGetCurrentThreadWin32Thread();
240    ThreadQueue = pti->MessageQueue;
241 
242    if(Window && ThreadQueue->CaretInfo.hWnd != Window->head.h)
243    {
244       EngSetLastError(ERROR_ACCESS_DENIED);
245       return FALSE;
246    }
247 
248    if(ThreadQueue->CaretInfo.Visible)
249    {
250       PWND pwnd = UserGetWindowObject(ThreadQueue->CaretInfo.hWnd);
251       IntKillTimer(pwnd, IDCARETTIMER, TRUE);
252 
253       co_IntHideCaret(&ThreadQueue->CaretInfo);
254       ThreadQueue->CaretInfo.Visible = 0;
255       ThreadQueue->CaretInfo.Showing = 0;
256    }
257 
258    return TRUE;
259 }
260 
261 /* Win: zzzShowCaret */
262 BOOL FASTCALL co_UserShowCaret(PWND Window OPTIONAL)
263 {
264    PTHREADINFO pti;
265    PUSER_MESSAGE_QUEUE ThreadQueue;
266    PWND pWnd = NULL;
267 
268    if (Window) ASSERT_REFS_CO(Window);
269 
270    if(Window && Window->head.pti->pEThread != PsGetCurrentThread())
271    {
272       EngSetLastError(ERROR_ACCESS_DENIED);
273       return FALSE;
274    }
275 
276    pti = PsGetCurrentThreadWin32Thread();
277    ThreadQueue = pti->MessageQueue;
278 
279    if(Window && ThreadQueue->CaretInfo.hWnd != Window->head.h)
280    {
281       EngSetLastError(ERROR_ACCESS_DENIED);
282       return FALSE;
283    }
284 
285    if (!ThreadQueue->CaretInfo.Visible)
286    {
287       ThreadQueue->CaretInfo.Visible = 1;
288       pWnd = ValidateHwndNoErr(ThreadQueue->CaretInfo.hWnd);
289       if (!ThreadQueue->CaretInfo.Showing && pWnd)
290       {
291          IntNotifyWinEvent(EVENT_OBJECT_SHOW, pWnd, OBJID_CARET, OBJID_CARET, 0);
292       }
293       if ((INT)gpsi->dtCaretBlink > 0)
294       {
295           IntSetTimer(pWnd, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM);
296       }
297       else if (ThreadQueue->CaretInfo.Visible)
298       {
299           ThreadQueue->CaretInfo.Showing = 1;
300           co_IntDrawCaret(pWnd, &ThreadQueue->CaretInfo);
301       }
302    }
303    return TRUE;
304 }
305 
306 /* SYSCALLS *****************************************************************/
307 
308 BOOL
309 APIENTRY
310 NtUserCreateCaret(
311    HWND hWnd,
312    HBITMAP hBitmap,
313    int nWidth,
314    int nHeight)
315 {
316    PWND Window;
317    PTHREADINFO pti;
318    PUSER_MESSAGE_QUEUE ThreadQueue;
319    BOOL Ret = FALSE;
320 
321    TRACE("Enter NtUserCreateCaret\n");
322    UserEnterExclusive();
323 
324    if(!(Window = UserGetWindowObject(hWnd)))
325    {
326       goto Exit; // Return FALSE
327    }
328 
329    if(Window->head.pti->pEThread != PsGetCurrentThread())
330    {
331       EngSetLastError(ERROR_ACCESS_DENIED);
332       goto Exit; // Return FALSE
333    }
334 
335    pti = PsGetCurrentThreadWin32Thread();
336    ThreadQueue = pti->MessageQueue;
337 
338    if (ThreadQueue->CaretInfo.Visible)
339    {
340       IntKillTimer(Window, IDCARETTIMER, TRUE);
341       co_IntHideCaret(&ThreadQueue->CaretInfo);
342    }
343 
344    ThreadQueue->CaretInfo.hWnd = hWnd;
345    if(hBitmap)
346    {
347       ThreadQueue->CaretInfo.Bitmap = hBitmap;
348       ThreadQueue->CaretInfo.Size.cx = ThreadQueue->CaretInfo.Size.cy = 0;
349    }
350    else
351    {
352       if (nWidth == 0)
353       {
354           nWidth = UserGetSystemMetrics(SM_CXBORDER);
355       }
356       if (nHeight == 0)
357       {
358           nHeight = UserGetSystemMetrics(SM_CYBORDER);
359       }
360       ThreadQueue->CaretInfo.Bitmap = (HBITMAP)0;
361       ThreadQueue->CaretInfo.Size.cx = nWidth;
362       ThreadQueue->CaretInfo.Size.cy = nHeight;
363    }
364    ThreadQueue->CaretInfo.Visible = 0;
365    ThreadQueue->CaretInfo.Showing = 0;
366 
367    IntSetTimer(Window, IDCARETTIMER, gpsi->dtCaretBlink, CaretSystemTimerProc, TMRF_SYSTEM);
368 
369    IntNotifyWinEvent(EVENT_OBJECT_CREATE, Window, OBJID_CARET, CHILDID_SELF, 0);
370 
371    Ret = TRUE;
372 
373 Exit:
374    TRACE("Leave NtUserCreateCaret, ret=%i\n", Ret);
375    UserLeave();
376    return Ret;
377 }
378 
379 UINT
380 APIENTRY
381 NtUserGetCaretBlinkTime(VOID)
382 {
383    UINT ret;
384 
385    UserEnterShared();
386 
387    ret = gpsi->dtCaretBlink;
388 
389    UserLeave();
390 
391    return ret;
392 }
393 
394 BOOL
395 APIENTRY
396 NtUserGetCaretPos(
397    LPPOINT lpPoint)
398 {
399    PTHREADINFO pti;
400    PUSER_MESSAGE_QUEUE ThreadQueue;
401    NTSTATUS Status;
402    BOOL Ret = TRUE;
403 
404    TRACE("Enter NtUserGetCaretPos\n");
405    UserEnterShared();
406 
407    pti = PsGetCurrentThreadWin32Thread();
408    ThreadQueue = pti->MessageQueue;
409 
410    Status = MmCopyToCaller(lpPoint, &ThreadQueue->CaretInfo.Pos, sizeof(POINT));
411    if(!NT_SUCCESS(Status))
412    {
413       SetLastNtError(Status);
414       Ret = FALSE;
415    }
416 
417    TRACE("Leave NtUserGetCaretPos, ret=%i\n", Ret);
418    UserLeave();
419    return Ret;
420 }
421 
422 BOOL
423 APIENTRY
424 NtUserShowCaret(HWND hWnd OPTIONAL)
425 {
426    PWND Window = NULL;
427    USER_REFERENCE_ENTRY Ref;
428    BOOL ret = FALSE;
429 
430    TRACE("Enter NtUserShowCaret\n");
431    UserEnterExclusive();
432 
433    if(hWnd && !(Window = UserGetWindowObject(hWnd)))
434    {
435       goto Exit; // Return FALSE
436    }
437 
438    if (Window) UserRefObjectCo(Window, &Ref);
439 
440    ret = co_UserShowCaret(Window);
441 
442    if (Window) UserDerefObjectCo(Window);
443 
444 Exit:
445    TRACE("Leave NtUserShowCaret, ret=%i\n", ret);
446    UserLeave();
447    return ret;
448 }
449 
450 BOOL
451 APIENTRY
452 NtUserHideCaret(HWND hWnd OPTIONAL)
453 {
454    PWND Window = NULL;
455    USER_REFERENCE_ENTRY Ref;
456    BOOL ret = FALSE;
457 
458    TRACE("Enter NtUserHideCaret\n");
459    UserEnterExclusive();
460 
461    if(hWnd && !(Window = UserGetWindowObject(hWnd)))
462    {
463       goto Exit; // Return FALSE
464    }
465 
466    if (Window) UserRefObjectCo(Window, &Ref);
467 
468    ret = co_UserHideCaret(Window);
469 
470    if (Window) UserDerefObjectCo(Window);
471 
472 Exit:
473    TRACE("Leave NtUserHideCaret, ret=%i\n", ret);
474    UserLeave();
475    return ret;
476 }
477