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