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