Fixed missing wine_tsx11_unlock() on error path.
[wine] / dlls / user / 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "wine/server.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(caret);
34
35 typedef struct
36 {
37     HBITMAP  hBmp;
38     UINT     timeout;
39 } CARET;
40
41 static CARET Caret = { 0, 500 };
42
43 #define TIMERID 0xffff  /* system timer id for the caret */
44
45
46 /*****************************************************************
47  *               CARET_DisplayCaret
48  */
49 static void CARET_DisplayCaret( HWND hwnd, const RECT *r )
50 {
51     HDC hdc;
52     HDC hCompDC;
53
54     /* do not use DCX_CACHE here, for x,y,width,height are in logical units */
55     if (!(hdc = GetDCEx( hwnd, 0, DCX_USESTYLE /*| DCX_CACHE*/ ))) return;
56     hCompDC = CreateCompatibleDC(hdc);
57     if (hCompDC)
58     {
59         HBITMAP hPrevBmp;
60
61         hPrevBmp = SelectObject(hCompDC, Caret.hBmp);
62         BitBlt(hdc, r->left, r->top, r->right-r->left, r->bottom-r->top, hCompDC, 0, 0, SRCINVERT);
63         SelectObject(hCompDC, hPrevBmp);
64         DeleteDC(hCompDC);
65     }
66     ReleaseDC( hwnd, hdc );
67 }
68
69
70 /*****************************************************************
71  *               CARET_Callback
72  */
73 static void CALLBACK CARET_Callback( HWND hwnd, UINT msg, UINT id, DWORD ctime)
74 {
75     BOOL ret;
76     RECT r;
77     int old_state = 0;
78     int hidden = 0;
79
80     SERVER_START_REQ( set_caret_info )
81     {
82         req->flags  = SET_CARET_STATE;
83         req->handle = hwnd;
84         req->x      = 0;
85         req->y      = 0;
86         req->hide   = 0;
87         req->state  = -1;  /* toggle current state */
88         if ((ret = !wine_server_call( req )))
89         {
90             hwnd      = reply->full_handle;
91             r.left    = reply->old_rect.left;
92             r.top     = reply->old_rect.top;
93             r.right   = reply->old_rect.right;
94             r.bottom  = reply->old_rect.bottom;
95             old_state = reply->old_state;
96             hidden    = reply->old_hide;
97         }
98     }
99     SERVER_END_REQ;
100
101     if (ret && !hidden) CARET_DisplayCaret( hwnd, &r );
102 }
103
104
105 /*****************************************************************
106  *              CreateCaret (USER32.@)
107  */
108 BOOL WINAPI CreateCaret( HWND hwnd, HBITMAP bitmap, INT width, INT height )
109 {
110     BOOL ret;
111     RECT r;
112     int old_state = 0;
113     int hidden = 0;
114     HBITMAP hBmp = 0;
115     HWND prev = 0;
116
117     TRACE("hwnd=%p\n", hwnd);
118
119     if (!hwnd) return FALSE;
120
121     if (bitmap && (bitmap != (HBITMAP)1))
122     {
123         BITMAP bmp;
124         if (!GetObjectA( bitmap, sizeof(bmp), &bmp )) return FALSE;
125         width = bmp.bmWidth;
126         height = bmp.bmHeight;
127         bmp.bmBits = NULL;
128         hBmp = CreateBitmapIndirect(&bmp);
129         if (hBmp)
130         {
131             /* copy the bitmap */
132             LPBYTE buf = HeapAlloc(GetProcessHeap(), 0, bmp.bmWidthBytes * bmp.bmHeight);
133             GetBitmapBits(bitmap, bmp.bmWidthBytes * bmp.bmHeight, buf);
134             SetBitmapBits(hBmp, bmp.bmWidthBytes * bmp.bmHeight, buf);
135             HeapFree(GetProcessHeap(), 0, buf);
136         }
137     }
138     else
139     {
140         HDC hdc;
141
142         if (!width) width = GetSystemMetrics(SM_CXBORDER);
143         if (!height) height = GetSystemMetrics(SM_CYBORDER);
144
145         /* create the uniform bitmap on the fly */
146         hdc = GetDC(hwnd);
147         if (hdc)
148         {
149             HDC hMemDC = CreateCompatibleDC(hdc);
150             if (hMemDC)
151             {
152                 if ((hBmp = CreateCompatibleBitmap(hMemDC, width, height )))
153                 {
154                     HBITMAP hPrevBmp = SelectObject(hMemDC, hBmp);
155                     SetRect( &r, 0, 0, width, height );
156                     FillRect(hMemDC, &r, (HBRUSH)((bitmap ? COLOR_GRAYTEXT : COLOR_WINDOW) + 1));
157                     SelectObject(hMemDC, hPrevBmp);
158                 }
159                 DeleteDC(hMemDC);
160             }
161             ReleaseDC(hwnd, hdc);
162         }
163     }
164     if (!hBmp) return FALSE;
165
166     SERVER_START_REQ( set_caret_window )
167     {
168         req->handle = hwnd;
169         req->width  = width;
170         req->height = height;
171         if ((ret = !wine_server_call_err( req )))
172         {
173             prev      = reply->previous;
174             r.left    = reply->old_rect.left;
175             r.top     = reply->old_rect.top;
176             r.right   = reply->old_rect.right;
177             r.bottom  = reply->old_rect.bottom;
178             old_state = reply->old_state;
179             hidden    = reply->old_hide;
180         }
181     }
182     SERVER_END_REQ;
183     if (!ret) return FALSE;
184
185     if (prev && !hidden)  /* hide the previous one */
186     {
187         /* FIXME: won't work if prev belongs to a different process */
188         KillSystemTimer( prev, TIMERID );
189         if (old_state) CARET_DisplayCaret( prev, &r );
190     }
191
192     if (Caret.hBmp) DeleteObject( Caret.hBmp );
193     Caret.hBmp = hBmp;
194     Caret.timeout = GetProfileIntA( "windows", "CursorBlinkRate", 500 );
195     return TRUE;
196 }
197
198
199 /*****************************************************************
200  *              DestroyCaret (USER32.@)
201  */
202 BOOL WINAPI DestroyCaret(void)
203 {
204     BOOL ret;
205     HWND prev = 0;
206     RECT r;
207     int old_state = 0;
208     int hidden = 0;
209
210     SERVER_START_REQ( set_caret_window )
211     {
212         req->handle = 0;
213         req->width  = 0;
214         req->height = 0;
215         if ((ret = !wine_server_call_err( req )))
216         {
217             prev      = reply->previous;
218             r.left    = reply->old_rect.left;
219             r.top     = reply->old_rect.top;
220             r.right   = reply->old_rect.right;
221             r.bottom  = reply->old_rect.bottom;
222             old_state = reply->old_state;
223             hidden    = reply->old_hide;
224         }
225     }
226     SERVER_END_REQ;
227
228     if (ret && prev && !hidden)
229     {
230         /* FIXME: won't work if prev belongs to a different process */
231         KillSystemTimer( prev, TIMERID );
232         if (old_state) CARET_DisplayCaret( prev, &r );
233     }
234     if (Caret.hBmp) DeleteObject( Caret.hBmp );
235     Caret.hBmp = 0;
236     return ret;
237 }
238
239
240 /*****************************************************************
241  *              SetCaretPos (USER32.@)
242  */
243 BOOL WINAPI SetCaretPos( INT x, INT y )
244 {
245     BOOL ret;
246     HWND hwnd = 0;
247     RECT r;
248     int old_state = 0;
249     int hidden = 0;
250
251     SERVER_START_REQ( set_caret_info )
252     {
253         req->flags  = SET_CARET_POS|SET_CARET_STATE;
254         req->handle = 0;
255         req->x      = x;
256         req->y      = y;
257         req->hide   = 0;
258         req->state  = 1;
259         if ((ret = !wine_server_call_err( req )))
260         {
261             hwnd      = reply->full_handle;
262             r.left    = reply->old_rect.left;
263             r.top     = reply->old_rect.top;
264             r.right   = reply->old_rect.right;
265             r.bottom  = reply->old_rect.bottom;
266             old_state = reply->old_state;
267             hidden    = reply->old_hide;
268         }
269     }
270     SERVER_END_REQ;
271     if (ret && !hidden)
272     {
273         if (old_state) CARET_DisplayCaret( hwnd, &r );
274         r.right += x - r.left;
275         r.bottom += y - r.top;
276         r.left = x;
277         r.top = y;
278         CARET_DisplayCaret( hwnd, &r );
279         SetSystemTimer( hwnd, TIMERID, Caret.timeout, CARET_Callback );
280     }
281     return ret;
282 }
283
284
285 /*****************************************************************
286  *              HideCaret (USER32.@)
287  */
288 BOOL WINAPI HideCaret( HWND hwnd )
289 {
290     BOOL ret;
291     RECT r;
292     int old_state = 0;
293     int hidden = 0;
294
295     SERVER_START_REQ( set_caret_info )
296     {
297         req->flags  = SET_CARET_HIDE|SET_CARET_STATE;
298         req->handle = hwnd;
299         req->x      = 0;
300         req->y      = 0;
301         req->hide   = 1;
302         req->state  = 0;
303         if ((ret = !wine_server_call_err( req )))
304         {
305             hwnd      = reply->full_handle;
306             r.left    = reply->old_rect.left;
307             r.top     = reply->old_rect.top;
308             r.right   = reply->old_rect.right;
309             r.bottom  = reply->old_rect.bottom;
310             old_state = reply->old_state;
311             hidden    = reply->old_hide;
312         }
313     }
314     SERVER_END_REQ;
315
316     if (ret && !hidden)
317     {
318         if (old_state) CARET_DisplayCaret( hwnd, &r );
319         KillSystemTimer( hwnd, TIMERID );
320     }
321     return ret;
322 }
323
324
325 /*****************************************************************
326  *              ShowCaret (USER32.@)
327  */
328 BOOL WINAPI ShowCaret( HWND hwnd )
329 {
330     BOOL ret;
331     RECT r;
332     int old_state = 0;
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 = 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      = 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             old_state = reply->old_state;
351             hidden    = reply->old_hide;
352         }
353     }
354     SERVER_END_REQ;
355
356     if (ret && (hidden == 1))  /* hidden was 1 so it's now 0 */
357     {
358         CARET_DisplayCaret( hwnd, &r );
359         SetSystemTimer( hwnd, TIMERID, Caret.timeout, CARET_Callback );
360     }
361     return ret;
362 }
363
364
365 /*****************************************************************
366  *              GetCaretPos (USER32.@)
367  */
368 BOOL WINAPI GetCaretPos( LPPOINT pt )
369 {
370     BOOL ret;
371
372     SERVER_START_REQ( set_caret_info )
373     {
374         req->flags  = 0;  /* don't set anything */
375         req->handle = 0;
376         req->x      = 0;
377         req->y      = 0;
378         req->hide   = 0;
379         req->state  = 0;
380         if ((ret = !wine_server_call_err( req )))
381         {
382             pt->x = reply->old_rect.left;
383             pt->y = reply->old_rect.top;
384         }
385     }
386     SERVER_END_REQ;
387     return ret;
388 }
389
390
391 /*****************************************************************
392  *              SetCaretBlinkTime (USER32.@)
393  */
394 BOOL WINAPI SetCaretBlinkTime( UINT msecs )
395 {
396     TRACE("msecs=%d\n", msecs);
397
398     Caret.timeout = msecs;
399 /*    if (Caret.hwnd) CARET_SetTimer(); FIXME */
400     return TRUE;
401 }
402
403
404 /*****************************************************************
405  *              GetCaretBlinkTime (USER32.@)
406  */
407 UINT WINAPI GetCaretBlinkTime(void)
408 {
409     return Caret.timeout;
410 }