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