gdi32: Let user32 specify the device rectangle when setting the visible region.
[wine] / dlls / user32 / caret.c
1 /*
2  * Caret functions
3  *
4  * Copyright 1993 David Metcalfe
5  * Copyright 1996 Frans van Dorsselaer
6  * Copyright 2001 Eric Pouech
7  * Copyright 2002 Alexandre Julliard
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <stdarg.h>
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "wine/server.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(caret);
37
38 typedef struct
39 {
40     HBITMAP  hBmp;
41     UINT     timeout;
42 } CARET;
43
44 static CARET Caret = { 0, 500 };
45
46 #define TIMERID 0xffff  /* system timer id for the caret */
47
48
49 /*****************************************************************
50  *               CARET_DisplayCaret
51  */
52 static void CARET_DisplayCaret( HWND hwnd, const RECT *r )
53 {
54     HDC hdc;
55     HDC hCompDC;
56
57     /* do not use DCX_CACHE here, for x,y,width,height are in logical units */
58     if (!(hdc = GetDCEx( hwnd, 0, DCX_USESTYLE /*| DCX_CACHE*/ ))) return;
59     hCompDC = CreateCompatibleDC(hdc);
60     if (hCompDC)
61     {
62         HBITMAP hPrevBmp;
63
64         hPrevBmp = SelectObject(hCompDC, Caret.hBmp);
65         BitBlt(hdc, r->left, r->top, r->right-r->left, r->bottom-r->top, hCompDC, 0, 0, SRCINVERT);
66         SelectObject(hCompDC, hPrevBmp);
67         DeleteDC(hCompDC);
68     }
69     ReleaseDC( hwnd, hdc );
70 }
71
72
73 /*****************************************************************
74  *               CARET_Callback
75  */
76 static void CALLBACK CARET_Callback( HWND hwnd, UINT msg, UINT_PTR id, DWORD ctime)
77 {
78     BOOL ret;
79     RECT r;
80     int hidden = 0;
81
82     SERVER_START_REQ( set_caret_info )
83     {
84         req->flags  = SET_CARET_STATE;
85         req->handle = wine_server_user_handle( hwnd );
86         req->x      = 0;
87         req->y      = 0;
88         req->hide   = 0;
89         req->state  = -1;  /* toggle current state */
90         if ((ret = !wine_server_call( req )))
91         {
92             hwnd      = wine_server_ptr_handle( reply->full_handle );
93             r.left    = reply->old_rect.left;
94             r.top     = reply->old_rect.top;
95             r.right   = reply->old_rect.right;
96             r.bottom  = reply->old_rect.bottom;
97             hidden    = reply->old_hide;
98         }
99     }
100     SERVER_END_REQ;
101
102     if (ret && !hidden) CARET_DisplayCaret( hwnd, &r );
103 }
104
105
106 /*****************************************************************
107  *              CreateCaret (USER32.@)
108  */
109 BOOL WINAPI CreateCaret( HWND hwnd, HBITMAP bitmap, INT width, INT height )
110 {
111     BOOL ret;
112     RECT r;
113     int old_state = 0;
114     int hidden = 0;
115     HBITMAP hBmp = 0;
116     HWND prev = 0;
117
118     TRACE("hwnd=%p\n", hwnd);
119
120     if (!hwnd) return FALSE;
121
122     if (bitmap && (bitmap != (HBITMAP)1))
123     {
124         BITMAP bmp;
125         if (!GetObjectA( bitmap, sizeof(bmp), &bmp )) return FALSE;
126         width = bmp.bmWidth;
127         height = bmp.bmHeight;
128         bmp.bmBits = NULL;
129         hBmp = CreateBitmapIndirect(&bmp);
130         if (hBmp)
131         {
132             /* copy the bitmap */
133             LPBYTE buf = HeapAlloc(GetProcessHeap(), 0, bmp.bmWidthBytes * bmp.bmHeight);
134             GetBitmapBits(bitmap, bmp.bmWidthBytes * bmp.bmHeight, buf);
135             SetBitmapBits(hBmp, bmp.bmWidthBytes * bmp.bmHeight, buf);
136             HeapFree(GetProcessHeap(), 0, buf);
137         }
138     }
139     else
140     {
141         HDC hdc;
142
143         if (!width) width = GetSystemMetrics(SM_CXBORDER);
144         if (!height) height = GetSystemMetrics(SM_CYBORDER);
145
146         /* create the uniform bitmap on the fly */
147         hdc = GetDC(hwnd);
148         if (hdc)
149         {
150             HDC hMemDC = CreateCompatibleDC(hdc);
151             if (hMemDC)
152             {
153                 if ((hBmp = CreateCompatibleBitmap(hMemDC, width, height )))
154                 {
155                     HBITMAP hPrevBmp = SelectObject(hMemDC, hBmp);
156                     SetRect( &r, 0, 0, width, height );
157                     FillRect(hMemDC, &r, bitmap ? GetStockObject(GRAY_BRUSH) : GetStockObject(WHITE_BRUSH));
158                     SelectObject(hMemDC, hPrevBmp);
159                 }
160                 DeleteDC(hMemDC);
161             }
162             ReleaseDC(hwnd, hdc);
163         }
164     }
165     if (!hBmp) return FALSE;
166
167     SERVER_START_REQ( set_caret_window )
168     {
169         req->handle = wine_server_user_handle( hwnd );
170         req->width  = width;
171         req->height = height;
172         if ((ret = !wine_server_call_err( req )))
173         {
174             prev      = wine_server_ptr_handle( reply->previous );
175             r.left    = reply->old_rect.left;
176             r.top     = reply->old_rect.top;
177             r.right   = reply->old_rect.right;
178             r.bottom  = reply->old_rect.bottom;
179             old_state = reply->old_state;
180             hidden    = reply->old_hide;
181         }
182     }
183     SERVER_END_REQ;
184     if (!ret) return FALSE;
185
186     if (prev && !hidden)  /* hide the previous one */
187     {
188         /* FIXME: won't work if prev belongs to a different process */
189         KillSystemTimer( prev, TIMERID );
190         if (old_state) CARET_DisplayCaret( prev, &r );
191     }
192
193     if (Caret.hBmp) DeleteObject( Caret.hBmp );
194     Caret.hBmp = hBmp;
195     Caret.timeout = GetProfileIntA( "windows", "CursorBlinkRate", 500 );
196     return TRUE;
197 }
198
199
200 /*****************************************************************
201  *              DestroyCaret (USER32.@)
202  */
203 BOOL WINAPI DestroyCaret(void)
204 {
205     BOOL ret;
206     HWND prev = 0;
207     RECT r;
208     int old_state = 0;
209     int hidden = 0;
210
211     SERVER_START_REQ( set_caret_window )
212     {
213         req->handle = 0;
214         req->width  = 0;
215         req->height = 0;
216         if ((ret = !wine_server_call_err( req )))
217         {
218             prev      = wine_server_ptr_handle( reply->previous );
219             r.left    = reply->old_rect.left;
220             r.top     = reply->old_rect.top;
221             r.right   = reply->old_rect.right;
222             r.bottom  = reply->old_rect.bottom;
223             old_state = reply->old_state;
224             hidden    = reply->old_hide;
225         }
226     }
227     SERVER_END_REQ;
228
229     if (ret && prev && !hidden)
230     {
231         /* FIXME: won't work if prev belongs to a different process */
232         KillSystemTimer( prev, TIMERID );
233         if (old_state) CARET_DisplayCaret( prev, &r );
234     }
235     if (Caret.hBmp) DeleteObject( Caret.hBmp );
236     Caret.hBmp = 0;
237     return ret;
238 }
239
240
241 /*****************************************************************
242  *              SetCaretPos (USER32.@)
243  */
244 BOOL WINAPI SetCaretPos( INT x, INT y )
245 {
246     BOOL ret;
247     HWND hwnd = 0;
248     RECT r;
249     int old_state = 0;
250     int hidden = 0;
251
252     SERVER_START_REQ( set_caret_info )
253     {
254         req->flags  = SET_CARET_POS|SET_CARET_STATE;
255         req->handle = 0;
256         req->x      = x;
257         req->y      = y;
258         req->hide   = 0;
259         req->state  = 1;
260         if ((ret = !wine_server_call_err( req )))
261         {
262             hwnd      = wine_server_ptr_handle( reply->full_handle );
263             r.left    = reply->old_rect.left;
264             r.top     = reply->old_rect.top;
265             r.right   = reply->old_rect.right;
266             r.bottom  = reply->old_rect.bottom;
267             old_state = reply->old_state;
268             hidden    = reply->old_hide;
269         }
270     }
271     SERVER_END_REQ;
272     if (ret && !hidden && (x != r.left || y != r.top))
273     {
274         if (old_state) CARET_DisplayCaret( hwnd, &r );
275         r.right += x - r.left;
276         r.bottom += y - r.top;
277         r.left = x;
278         r.top = y;
279         CARET_DisplayCaret( hwnd, &r );
280         SetSystemTimer( hwnd, TIMERID, Caret.timeout, CARET_Callback );
281     }
282     return ret;
283 }
284
285
286 /*****************************************************************
287  *              HideCaret (USER32.@)
288  */
289 BOOL WINAPI HideCaret( HWND hwnd )
290 {
291     BOOL ret;
292     RECT r;
293     int old_state = 0;
294     int hidden = 0;
295
296     SERVER_START_REQ( set_caret_info )
297     {
298         req->flags  = SET_CARET_HIDE|SET_CARET_STATE;
299         req->handle = wine_server_user_handle( hwnd );
300         req->x      = 0;
301         req->y      = 0;
302         req->hide   = 1;
303         req->state  = 0;
304         if ((ret = !wine_server_call_err( req )))
305         {
306             hwnd      = wine_server_ptr_handle( reply->full_handle );
307             r.left    = reply->old_rect.left;
308             r.top     = reply->old_rect.top;
309             r.right   = reply->old_rect.right;
310             r.bottom  = reply->old_rect.bottom;
311             old_state = reply->old_state;
312             hidden    = reply->old_hide;
313         }
314     }
315     SERVER_END_REQ;
316
317     if (ret && !hidden)
318     {
319         if (old_state) CARET_DisplayCaret( hwnd, &r );
320         KillSystemTimer( hwnd, TIMERID );
321     }
322     return ret;
323 }
324
325
326 /*****************************************************************
327  *              ShowCaret (USER32.@)
328  */
329 BOOL WINAPI ShowCaret( HWND hwnd )
330 {
331     BOOL ret;
332     RECT r;
333     int hidden = 0;
334
335     SERVER_START_REQ( set_caret_info )
336     {
337         req->flags  = SET_CARET_HIDE|SET_CARET_STATE;
338         req->handle = wine_server_user_handle( hwnd );
339         req->x      = 0;
340         req->y      = 0;
341         req->hide   = -1;
342         req->state  = 1;
343         if ((ret = !wine_server_call_err( req )))
344         {
345             hwnd      = wine_server_ptr_handle( reply->full_handle );
346             r.left    = reply->old_rect.left;
347             r.top     = reply->old_rect.top;
348             r.right   = reply->old_rect.right;
349             r.bottom  = reply->old_rect.bottom;
350             hidden    = reply->old_hide;
351         }
352     }
353     SERVER_END_REQ;
354
355     if (ret && (hidden == 1))  /* hidden was 1 so it's now 0 */
356     {
357         CARET_DisplayCaret( hwnd, &r );
358         SetSystemTimer( hwnd, TIMERID, Caret.timeout, CARET_Callback );
359     }
360     return ret;
361 }
362
363
364 /*****************************************************************
365  *              GetCaretPos (USER32.@)
366  */
367 BOOL WINAPI GetCaretPos( LPPOINT pt )
368 {
369     BOOL ret;
370
371     SERVER_START_REQ( set_caret_info )
372     {
373         req->flags  = 0;  /* don't set anything */
374         req->handle = 0;
375         req->x      = 0;
376         req->y      = 0;
377         req->hide   = 0;
378         req->state  = 0;
379         if ((ret = !wine_server_call_err( req )))
380         {
381             pt->x = reply->old_rect.left;
382             pt->y = reply->old_rect.top;
383         }
384     }
385     SERVER_END_REQ;
386     return ret;
387 }
388
389
390 /*****************************************************************
391  *              SetCaretBlinkTime (USER32.@)
392  */
393 BOOL WINAPI SetCaretBlinkTime( UINT msecs )
394 {
395     TRACE("msecs=%d\n", msecs);
396
397     Caret.timeout = msecs;
398 /*    if (Caret.hwnd) CARET_SetTimer(); FIXME */
399     return TRUE;
400 }
401
402
403 /*****************************************************************
404  *              GetCaretBlinkTime (USER32.@)
405  */
406 UINT WINAPI GetCaretBlinkTime(void)
407 {
408     return Caret.timeout;
409 }