xref: /reactos/base/applications/mstsc/win32.c (revision 40462c92)
1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    win32 calls
4    Copyright (C) Jay Sorg 2006
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "precomp.h"
22 
23 #include "uimain.h"
24 
25 extern char g_username[];
26 extern char g_hostname[];
27 extern char g_servername[];
28 extern char g_password[];
29 extern char g_shell[];
30 extern char g_directory[];
31 extern char g_domain[];
32 extern int g_width;
33 extern int g_height;
34 extern int g_tcp_sck;
35 extern int g_server_depth;
36 extern int g_tcp_port_rdp; /* in tcp.c */
37 extern int pal_entries[];
38 
39 static HWND g_Wnd = 0;
40 static HINSTANCE g_Instance = 0;
41 static HCURSOR g_cursor = 0;
42 static int g_block = 0;
43 static int g_xoff = 0; /* offset from window to client coords */
44 static int g_yoff = 0;
45 static int g_xscroll = 0; /* current scroll position */
46 static int g_yscroll = 0;
47 static int g_screen_width = 0;
48 static int g_screen_height = 0;
49 static int g_wnd_cwidth = 0; /* set from WM_SIZE */
50 static int g_wnd_cheight = 0;
51 static int g_fullscreen = 0;
52 static int g_workarea = 0;
53 static int g_mousex = 0; /* in client coords */
54 static int g_mousey = 0;
55 //static int g_width_height_set = 0;
56 
57 static int g_clip_left = 0;
58 static int g_clip_top = 0;
59 static int g_clip_right = 800;
60 static int g_clip_bottom = 600;
61 static RECT g_wnd_clip; /* this client area of whats actually visible */
62                         /* set from WM_SIZE */
63 
64 /*****************************************************************************/
65 static void
66 str_to_uni(TCHAR * sizex, char * size1)
67 {
68   int len;
69   int i;
70 
71   len = strlen(size1);
72   for (i = 0; i < len; i++)
73   {
74     sizex[i] = size1[i];
75   }
76   sizex[len] = 0;
77 }
78 
79 /*****************************************************************************/
80 static void
81 uni_to_str(char * sizex, TCHAR * size1)
82 {
83   int len;
84   int i;
85 
86   len = lstrlen(size1);
87   for (i = 0; i < len; i++)
88   {
89     sizex[i] = (char)size1[i];
90   }
91   sizex[len] = 0;
92 }
93 
94 /*****************************************************************************/
95 /* returns non zero if it processed something */
96 static int
97 check_sck(void)
98 {
99   fd_set rfds;
100   struct timeval tm;
101   int count;
102   int rv;
103 
104   rv = 0;
105   if (g_block == 0)
106   {
107     g_block = 1;
108     /* see if there really is data */
109     FD_ZERO(&rfds);
110     FD_SET((unsigned int)g_tcp_sck, &rfds);
111     ZeroMemory(&tm, sizeof(tm));
112     count = select(g_tcp_sck + 1, &rfds, 0, 0, &tm);
113     if (count > 0)
114     {
115       if (ui_read_wire())
116       {
117         rv = 1;
118       }
119       else
120       {
121         PostQuitMessage(0);
122       }
123     }
124     g_block = 0;
125   }
126   return rv;
127 }
128 
129 /*****************************************************************************/
130 void
131 mi_error(char * msg)
132 {
133 #ifdef WITH_DEBUG
134   printf(msg);
135 #else /* WITH_DEBUG */
136   TCHAR lmsg[512];
137   TCHAR ltitle[512];
138 
139   str_to_uni(lmsg, msg);
140   str_to_uni(ltitle, "Error");
141   MessageBox(g_Wnd, lmsg, ltitle, MB_OK);
142 #endif /* WITH_DEBUG */
143 }
144 
145 /*****************************************************************************/
146 static int
147 get_scan_code_from_ascii(int code)
148 {
149   int rv;
150 
151   rv = 0;
152   switch (code & 0xff)
153   {
154     case 0x30: rv = 0x0b; break; // 0
155     case 0x31: rv = 0x02; break; // 1
156     case 0x32: rv = 0x03; break; // 2
157     case 0x33: rv = 0x04; break; // 3
158     case 0x34: rv = 0x05; break; // 4
159     case 0x35: rv = 0x06; break; // 5
160     case 0x36: rv = 0x07; break; // 6
161     case 0x37: rv = 0x08; break; // 7
162     case 0x38: rv = 0x09; break; // 8
163     case 0x39: rv = 0x0a; break; // 9
164 
165     case 0xbd: rv = 0x0c; break; // -
166     case 0xbb: rv = 0x0d; break; // =
167     case 0x08: rv = 0x0e; break; // backspace
168     case 0x09: rv = 0x0f; break; // tab
169     case 0xdb: rv = 0x1b; break; // ]
170     case 0xdd: rv = 0x1a; break; // [
171     case 0x14: rv = 0x3a; break; // capslock
172     case 0xba: rv = 0x27; break; // ;
173     case 0xde: rv = 0x28; break; // '
174     case 0x10: rv = 0x2a; break; // shift
175     case 0xbc: rv = 0x33; break; // ,
176     case 0xbe: rv = 0x34; break; // .
177     case 0xbf: rv = 0x35; break; // /
178     case 0x0d: rv = 0x1c; break; // enter
179     case 0x27: rv = 0x4d; break; // arrow right
180     case 0x25: rv = 0x4b; break; // arrow left
181     case 0x26: rv = 0x48; break; // arrow up
182     case 0x28: rv = 0x50; break; // arrow down
183     case 0x20: rv = 0x39; break; // space
184     case 0xdc: rv = 0x2b; break; // backslash
185     case 0xc0: rv = 0x29; break; // `
186     case 0x11: rv = 0x1d; break; // ctl
187 
188     case 0x41: rv = 0x1e; break; // a
189     case 0x42: rv = 0x30; break; // b
190     case 0x43: rv = 0x2e; break; // c
191     case 0x44: rv = 0x20; break; // d
192     case 0x45: rv = 0x12; break; // e
193     case 0x46: rv = 0x21; break; // f
194     case 0x47: rv = 0x22; break; // g
195     case 0x48: rv = 0x23; break; // h
196     case 0x49: rv = 0x17; break; // i
197     case 0x4a: rv = 0x24; break; // j
198     case 0x4b: rv = 0x25; break; // k
199     case 0x4c: rv = 0x26; break; // l
200     case 0x4d: rv = 0x32; break; // m
201     case 0x4e: rv = 0x31; break; // n
202     case 0x4f: rv = 0x18; break; // o
203     case 0x50: rv = 0x19; break; // p
204     case 0x51: rv = 0x10; break; // q
205     case 0x52: rv = 0x13; break; // r
206     case 0x53: rv = 0x1f; break; // s
207     case 0x54: rv = 0x14; break; // t
208     case 0x55: rv = 0x16; break; // u
209     case 0x56: rv = 0x2f; break; // v
210     case 0x57: rv = 0x11; break; // w
211     case 0x58: rv = 0x2d; break; // x
212     case 0x59: rv = 0x15; break; // y
213     case 0x5a: rv = 0x2c; break; // z
214   }
215   return rv;
216 }
217 
218 /*****************************************************************************/
219 static void
220 mi_scroll(int dx, int dy)
221 {
222   HRGN rgn;
223 
224   rgn = CreateRectRgn(0, 0, 0, 0);
225   ScrollWindowEx(g_Wnd, dx, dy, 0, 0, rgn, 0, SW_ERASE);
226   InvalidateRgn(g_Wnd, rgn, 0);
227   DeleteObject(rgn);
228 }
229 
230 /*****************************************************************************/
231 int
232 mi_read_keyboard_state(void)
233 {
234   short keydata;
235   int code;
236 
237   code = 0;
238   keydata = GetKeyState(VK_SCROLL);
239   if (keydata & 0x0001)
240   {
241     code |= 1;
242   }
243   keydata = GetKeyState(VK_NUMLOCK);
244   if (keydata & 0x0001)
245   {
246     code |= 2;
247   }
248   keydata = GetKeyState(VK_CAPITAL);
249   if (keydata & 0x0001)
250   {
251     code |= 4;
252   }
253   return code;
254 }
255 
256 /*****************************************************************************/
257 static void
258 mi_check_modifier(void)
259 {
260   int code;
261 
262   code = mi_read_keyboard_state();
263   ui_set_modifier_state(code);
264 }
265 
266 /*****************************************************************************/
267 static LRESULT
268 handle_WM_SETCURSOR(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
269 {
270   if (g_mousex >= g_wnd_clip.left &&
271       g_mousey >= g_wnd_clip.top &&
272       g_mousex < g_wnd_clip.right &&
273       g_mousey < g_wnd_clip.bottom)
274   {
275     SetCursor(g_cursor);
276   }
277   /* need default behavoir here */
278   return DefWindowProc(hWnd, message, wParam, lParam);
279 }
280 
281 /*****************************************************************************/
282 static LRESULT
283 handle_WM_NCHITTEST(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
284 {
285   POINT pt;
286 
287   pt.x = LOWORD(lParam);
288   pt.y = HIWORD(lParam);
289   if (ScreenToClient(g_Wnd, &pt))
290   {
291     g_mousex = pt.x;
292     g_mousey = pt.y;
293   }
294   return DefWindowProc(hWnd, message, wParam, lParam);
295 }
296 
297 /*****************************************************************************/
298 static LRESULT
299 handle_WM_MOUSEMOVE(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
300 {
301   g_mousex = LOWORD(lParam);
302   g_mousey = HIWORD(lParam);
303   ui_mouse_move(g_mousex + g_xscroll, g_mousey + g_yscroll);
304   return 0;
305 }
306 
307 /*****************************************************************************/
308 static LRESULT
309 handle_WM_LBUTTONDOWN(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
310 {
311   g_mousex = LOWORD(lParam);
312   g_mousey = HIWORD(lParam);
313   ui_mouse_button(1, g_mousex + g_xscroll, g_mousey + g_yscroll, 1);
314   return 0;
315 }
316 
317 /*****************************************************************************/
318 static LRESULT
319 handle_WM_LBUTTONUP(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
320 {
321   g_mousex = LOWORD(lParam);
322   g_mousey = HIWORD(lParam);
323   ui_mouse_button(1, g_mousex + g_xscroll, g_mousey + g_yscroll, 0);
324   return 0;
325 }
326 
327 /*****************************************************************************/
328 static LRESULT
329 handle_WM_RBUTTONDOWN(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
330 {
331   g_mousex = LOWORD(lParam);
332   g_mousey = HIWORD(lParam);
333   ui_mouse_button(2, g_mousex + g_xscroll, g_mousey + g_yscroll, 1);
334   return 0;
335 }
336 
337 /*****************************************************************************/
338 static LRESULT
339 handle_WM_RBUTTONUP(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
340 {
341   g_mousex = LOWORD(lParam);
342   g_mousey = HIWORD(lParam);
343   ui_mouse_button(2, g_mousex + g_xscroll, g_mousey + g_yscroll, 0);
344   return 0;
345 }
346 
347 /*****************************************************************************/
348 static LRESULT
349 handle_WM_MBUTTONDOWN(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
350 {
351   g_mousex = LOWORD(lParam);
352   g_mousey = HIWORD(lParam);
353   ui_mouse_button(3, g_mousex + g_xscroll, g_mousey + g_yscroll, 1);
354   return 0;
355 }
356 
357 /*****************************************************************************/
358 static LRESULT
359 handle_WM_MBUTTONUP(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
360 {
361   g_mousex = LOWORD(lParam);
362   g_mousey = HIWORD(lParam);
363   ui_mouse_button(3, g_mousex + g_xscroll, g_mousey + g_yscroll, 0);
364   return 0;
365 }
366 
367 /*****************************************************************************/
368 static LRESULT
369 handle_WM_MOUSEWHEEL(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
370 {
371   int delta;
372 
373   delta = ((signed short)HIWORD(wParam)); /* GET_WHEEL_DELTA_WPARAM(wParam); */
374   if (delta > 0)
375   {
376     ui_mouse_button(4, 0, 0, 1);
377     ui_mouse_button(4, 0, 0, 0);
378   }
379   else
380   {
381     ui_mouse_button(5, 0, 0, 1);
382     ui_mouse_button(5, 0, 0, 0);
383   }
384   return 0;
385 }
386 
387 /*****************************************************************************/
388 static LRESULT
389 handle_WM_KEY(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
390 {
391   int scancode;
392   int ext;
393   int down;
394 
395   ext = HIWORD(lParam);
396   scancode = ext;
397   down = !(ext & 0x8000);
398   scancode &= 0xff;
399   if (scancode == 0)
400   {
401     scancode = get_scan_code_from_ascii(wParam);
402   }
403   ext &= 0x0100;
404   if (scancode == 0x0045) /* num lock */
405   {
406     ext &= ~0x0100;
407   }
408   if (down)
409   {
410     ui_key_down(scancode, ext);
411   }
412   else
413   {
414     ui_key_up(scancode, ext);
415   }
416   return 0;
417 }
418 
419 /*****************************************************************************/
420 static LRESULT
421 handle_WM_PAINT(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
422 {
423   PAINTSTRUCT ps;
424   RECT rect;
425   HBRUSH brush;
426 
427   BeginPaint(hWnd, &ps);
428   /* paint the area outside the rdp screen with one colour */
429   rect = ps.rcPaint;
430   rect.left = UI_MAX(rect.left, g_width);
431   if (!IsRectEmpty(&rect))
432   {
433     brush = CreateSolidBrush(RGB(0, 0, 255));
434     FillRect(ps.hdc, &rect, brush);
435     DeleteObject(brush);
436   }
437   rect = ps.rcPaint;
438   rect.top = UI_MAX(rect.top, g_height);
439   if (!IsRectEmpty(&rect))
440   {
441     brush = CreateSolidBrush(RGB(0, 0, 255));
442     FillRect(ps.hdc, &rect, brush);
443     DeleteObject(brush);
444   }
445   rect = ps.rcPaint;
446   EndPaint(hWnd, &ps);
447   ui_invalidate(rect.left + g_xscroll,
448                 rect.top + g_yscroll,
449                 (rect.right - rect.left) + 1,
450                 (rect.bottom - rect.top) + 1);
451   return 0;
452 }
453 
454 /*****************************************************************************/
455 static LRESULT
456 handle_WM_SIZE(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
457 {
458   int oldxscroll;
459   int oldyscroll;
460 
461   if (wParam == SIZE_MINIMIZED)
462   {
463     return DefWindowProc(hWnd, message, wParam, lParam);
464   }
465   g_wnd_cwidth = LOWORD(lParam); /* client width / height */
466   g_wnd_cheight = HIWORD(lParam);
467   g_wnd_clip.left = 0;
468   g_wnd_clip.top = 0;
469   g_wnd_clip.right = g_wnd_clip.left + g_wnd_cwidth;
470   g_wnd_clip.bottom = g_wnd_clip.top + g_wnd_cheight;
471   if (g_wnd_cwidth < g_width || g_wnd_cheight < g_height)
472   {
473     SetScrollRange(g_Wnd, SB_HORZ, 0, g_width - g_wnd_cwidth, 1);
474     SetScrollRange(g_Wnd, SB_VERT, 0, g_height - g_wnd_cheight, 1);
475   }
476   oldxscroll = g_xscroll;
477   oldyscroll = g_yscroll;
478   if (g_wnd_cwidth >= g_width)
479   {
480     g_xscroll = 0;
481   }
482   else
483   {
484     g_xscroll = UI_MIN(g_xscroll, g_width - g_wnd_cwidth);
485   }
486   if (g_wnd_cheight >= g_height)
487   {
488     g_yscroll = 0;
489   }
490   else
491   {
492     g_yscroll = UI_MIN(g_yscroll, g_height - g_wnd_cheight);
493   }
494   mi_scroll(oldxscroll - g_xscroll, oldyscroll - g_yscroll);
495   if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
496   {
497     /* check the caps, num, and scroll lock here */
498     mi_check_modifier();
499   }
500   return 0;
501 }
502 
503 /*****************************************************************************/
504 static LRESULT
505 handle_WM_SIZING(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
506 {
507   LPRECT prect;
508   int width;
509   int height;
510   int style;
511 
512   prect = (LPRECT) lParam; /* total window rect */
513   width = (prect->right - prect->left) - (g_xoff * 2);
514   height = (prect->bottom - prect->top) - (g_yoff + g_xoff);
515   if (height < g_height || width < g_width)
516   {
517     style = GetWindowLongPtr(g_Wnd, GWL_STYLE);
518     if (!(style & WS_HSCROLL))
519     {
520       style |= WS_HSCROLL | WS_VSCROLL;
521       SetWindowLongPtr(g_Wnd, GWL_STYLE, style);
522       g_xscroll = 0;
523       g_yscroll = 0;
524       SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
525       SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
526     }
527   }
528   else if (height >= g_height && width >= g_width)
529   {
530     style = GetWindowLongPtr(g_Wnd, GWL_STYLE);
531     if (style & WS_HSCROLL)
532     {
533       style &= ~WS_HSCROLL;
534       style &= ~WS_VSCROLL;
535       SetWindowLongPtr(g_Wnd, GWL_STYLE, style);
536       g_xscroll = 0;
537       g_yscroll = 0;
538     }
539   }
540   return 0;
541 }
542 
543 /*****************************************************************************/
544 static LRESULT
545 handle_WM_HSCROLL(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
546 {
547   int code;
548   int oldxscroll;
549 
550   code = (int) LOWORD(wParam); /* scroll bar value */
551   if (code == SB_LINELEFT)
552   {
553     oldxscroll = g_xscroll;
554     g_xscroll--;
555     g_xscroll = UI_MAX(g_xscroll, 0);
556     SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
557     mi_scroll(oldxscroll - g_xscroll, 0);
558   }
559   else if (code == SB_LINERIGHT)
560   {
561     oldxscroll = g_xscroll;
562     g_xscroll++;
563     g_xscroll = UI_MIN(g_xscroll, g_width - g_wnd_cwidth);
564     SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
565     mi_scroll(oldxscroll - g_xscroll, 0);
566   }
567   else if (code == SB_PAGELEFT)
568   {
569     oldxscroll = g_xscroll;
570     g_xscroll -= g_wnd_cwidth / 2;
571     g_xscroll = UI_MAX(g_xscroll, 0);
572     SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
573     mi_scroll(oldxscroll - g_xscroll, 0);
574   }
575   else if (code == SB_PAGERIGHT)
576   {
577     oldxscroll = g_xscroll;
578     g_xscroll += g_wnd_cwidth / 2;
579     g_xscroll = UI_MIN(g_xscroll, g_width - g_wnd_cwidth);
580     SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
581     mi_scroll(oldxscroll - g_xscroll, 0);
582   }
583   else if (code == SB_BOTTOM)
584   {
585     oldxscroll = g_xscroll;
586     g_xscroll = g_width - g_wnd_cwidth;
587     SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
588     mi_scroll(oldxscroll - g_xscroll, 0);
589   }
590   else if (code == SB_TOP)
591   {
592     oldxscroll = g_xscroll;
593     g_xscroll = 0;
594     SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
595     mi_scroll(oldxscroll - g_xscroll, 0);
596   }
597   else if (code == SB_THUMBPOSITION)
598   {
599     oldxscroll = g_xscroll;
600     g_xscroll = (signed short) HIWORD(wParam);
601     SetScrollPos(g_Wnd, SB_HORZ, g_xscroll, 1);
602     mi_scroll(oldxscroll - g_xscroll, 0);
603   }
604   return 0;
605 }
606 
607 /*****************************************************************************/
608 static LRESULT
609 handle_WM_VSCROLL(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
610 {
611   int code;
612   int oldyscroll;
613 
614   code = (int) LOWORD(wParam); /* scroll bar value */
615   if (code == SB_LINELEFT)
616   {
617     oldyscroll = g_yscroll;
618     g_yscroll--;
619     g_yscroll = UI_MAX(g_yscroll, 0);
620     SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
621     mi_scroll(0, oldyscroll - g_yscroll);
622   }
623   else if (code == SB_LINERIGHT)
624   {
625     oldyscroll = g_yscroll;
626     g_yscroll++;
627     g_yscroll = UI_MIN(g_yscroll, g_height - g_wnd_cheight);
628     SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
629     mi_scroll(0, oldyscroll - g_yscroll);
630   }
631   else if (code == SB_PAGELEFT)
632   {
633     oldyscroll = g_yscroll;
634     g_yscroll -= g_wnd_cheight / 2;
635     g_yscroll = UI_MAX(g_yscroll, 0);
636     SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
637     mi_scroll(0, oldyscroll - g_yscroll);
638   }
639   else if (code == SB_PAGERIGHT)
640   {
641     oldyscroll = g_yscroll;
642     g_yscroll += g_wnd_cheight / 2;
643     g_yscroll = UI_MIN(g_yscroll, g_height - g_wnd_cheight);
644     SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
645     mi_scroll(0, oldyscroll - g_yscroll);
646   }
647   else if (code == SB_BOTTOM)
648   {
649     oldyscroll = g_yscroll;
650     g_yscroll = g_height - g_wnd_cheight;
651     SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
652     mi_scroll(0, oldyscroll - g_yscroll);
653   }
654   else if (code == SB_TOP)
655   {
656     oldyscroll = g_yscroll;
657     g_yscroll = 0;
658     SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
659     mi_scroll(0, oldyscroll - g_yscroll);
660   }
661   else if (code == SB_THUMBPOSITION)
662   {
663     oldyscroll = g_yscroll;
664     g_yscroll = (signed short) HIWORD(wParam);
665     SetScrollPos(g_Wnd, SB_VERT, g_yscroll, 1);
666     mi_scroll(0, oldyscroll - g_yscroll);
667   }
668   return 0;
669 }
670 
671 
672 /*****************************************************************************/
673 LRESULT CALLBACK
674 WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
675 {
676   switch (message)
677   {
678     case WM_SETCURSOR:
679       return handle_WM_SETCURSOR(hWnd, message, wParam, lParam);
680     case 0x0084: /* WinCE don't have this WM_NCHITTEST: */
681       return handle_WM_NCHITTEST(hWnd, message, wParam, lParam);
682     case WM_MOUSEMOVE:
683       return handle_WM_MOUSEMOVE(hWnd, message, wParam, lParam);
684     case WM_LBUTTONDOWN:
685       return handle_WM_LBUTTONDOWN(hWnd, message, wParam, lParam);
686     case WM_LBUTTONUP:
687       return handle_WM_LBUTTONUP(hWnd, message, wParam, lParam);
688     case WM_RBUTTONDOWN:
689       return handle_WM_RBUTTONDOWN(hWnd, message, wParam, lParam);
690     case WM_RBUTTONUP:
691       return handle_WM_RBUTTONUP(hWnd, message, wParam, lParam);
692     case WM_MBUTTONDOWN:
693       return handle_WM_MBUTTONDOWN(hWnd, message, wParam, lParam);
694     case WM_MBUTTONUP:
695       return handle_WM_MBUTTONUP(hWnd, message, wParam, lParam);
696     /* some windows compilers don't have these defined like vc6 */
697     case 0x020a: /* WM_MOUSEWHEEL: */
698       return handle_WM_MOUSEWHEEL(hWnd, message, wParam, lParam);
699     case WM_KEYDOWN:
700     case WM_KEYUP:
701     case WM_SYSKEYDOWN:
702     case WM_SYSKEYUP:
703       return handle_WM_KEY(hWnd, message, wParam, lParam);
704     case WM_CHAR:
705     case WM_DEADCHAR:
706     case WM_SYSCHAR:
707     case WM_SYSDEADCHAR:
708       break;
709     case WM_PAINT:
710       return handle_WM_PAINT(hWnd, message, wParam, lParam);
711     case WM_DESTROY:
712       PostQuitMessage(0);
713       break;
714     case WM_APP + 1:
715     case WM_TIMER:
716       check_sck();
717       break;
718     case WM_SIZE:
719       return handle_WM_SIZE(hWnd, message, wParam, lParam);
720     case 532: /* not defined in wince WM_SIZING: */
721       return handle_WM_SIZING(hWnd, message, wParam, lParam);
722     case WM_HSCROLL:
723       return handle_WM_HSCROLL(hWnd, message, wParam, lParam);
724     case WM_VSCROLL:
725       return handle_WM_VSCROLL(hWnd, message, wParam, lParam);
726     case WM_SETFOCUS:
727       mi_check_modifier();
728       return DefWindowProc(hWnd, message, wParam, lParam);
729     default:
730       return DefWindowProc(hWnd, message, wParam, lParam);
731   }
732   return 0;
733 }
734 
735 /*****************************************************************************/
736 static HRGN
737 mi_clip(HDC dc)
738 {
739   HRGN rgn;
740 
741   rgn = CreateRectRgn(g_clip_left + g_xoff - g_xscroll,
742                       g_clip_top + g_yoff - g_yscroll,
743                       g_clip_right + g_xoff - g_xscroll,
744                       g_clip_bottom + g_yoff - g_yscroll);
745   SelectClipRgn(dc, rgn);
746   IntersectClipRect(dc, g_wnd_clip.left + g_xoff, g_wnd_clip.top + g_yoff,
747                     g_wnd_clip.right + g_xoff, g_wnd_clip.bottom + g_yoff);
748   return rgn;
749 }
750 
751 /*****************************************************************************/
752 /* returns non zero if ok */
753 int
754 mi_create_window(void)
755 {
756   RECT rc;
757   WNDCLASS wc;
758   TCHAR classname[512];
759   TCHAR caption[512];
760   DWORD style;
761   int x;
762   int y;
763   int w;
764   int h;
765 
766   if (g_Wnd != 0 || g_Instance != 0)
767   {
768     return 0;
769   }
770   g_Instance = GetModuleHandle(NULL);
771   ZeroMemory(&wc, sizeof(wc));
772   wc.lpfnWndProc = WndProc; /* points to window procedure */
773   /* name of window class */
774   str_to_uni(classname, "rdesktop");
775   wc.lpszClassName = classname;
776   str_to_uni(caption, "ReactOS Remote Desktop");
777   wc.hIcon = LoadIcon(g_Instance,
778                       MAKEINTRESOURCE(IDI_MSTSC));
779   /* Register the window class. */
780   if (!RegisterClass(&wc))
781   {
782     return 0; /* Failed to register window class */
783   }
784   rc.left = 0;
785   rc.right = rc.left + UI_MIN(g_width, g_screen_width);
786   rc.top = 0;
787   rc.bottom = rc.top + UI_MIN(g_height, g_screen_height);
788 
789   if (g_fullscreen)
790   {
791     style = WS_POPUP;
792   }
793   else
794   {
795     style = WS_OVERLAPPED | WS_CAPTION | WS_POPUP | WS_MINIMIZEBOX |
796             WS_SYSMENU | WS_SIZEBOX | WS_MAXIMIZEBOX;
797   }
798   if (g_screen_width < g_width || g_screen_height < g_height)
799   {
800     style |= WS_HSCROLL | WS_VSCROLL;
801   }
802   AdjustWindowRectEx(&rc, style, 0, 0);
803   x = CW_USEDEFAULT;
804   y = CW_USEDEFAULT;
805   w = rc.right - rc.left;
806   h = rc.bottom - rc.top;
807 
808   g_Wnd = CreateWindow(wc.lpszClassName, caption,
809                        style, x, y, w, h,
810                        (HWND) NULL, (HMENU) NULL, g_Instance,
811                        (LPVOID) NULL);
812   g_clip_left = 0;
813   g_clip_top = 0;
814   g_clip_right = g_clip_left + g_width;
815   g_clip_bottom = g_clip_top + g_height;
816   if (g_workarea)
817   {
818     ShowWindow(g_Wnd, SW_SHOWMAXIMIZED);
819   }
820   else
821   {
822     ShowWindow(g_Wnd, SW_SHOWNORMAL);
823   }
824   UpdateWindow(g_Wnd);
825 
826   WSAAsyncSelect(g_tcp_sck, g_Wnd, WM_APP + 1, FD_READ);
827   SetTimer(g_Wnd, 1, 333, 0);
828 
829   return 1;
830 }
831 
832 /*****************************************************************************/
833 int
834 mi_main_loop(void)
835 {
836   MSG msg;
837 
838   while (GetMessage(&msg, NULL, 0, 0))
839   {
840     TranslateMessage(&msg);
841     DispatchMessage(&msg);
842   }
843   return msg.wParam;
844 }
845 
846 /*****************************************************************************/
847 void
848 mi_warning(char * msg)
849 {
850 }
851 
852 /*****************************************************************************/
853 static void
854 mi_show_error(char * caption)
855 {
856   LPVOID lpMsgBuf;
857   TCHAR lcaption[512];
858 
859   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
860                 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
861                 (LPTSTR) &lpMsgBuf, 0, NULL);
862 #ifdef WITH_DEBUG
863   printf(lpMsgBuf);
864 #else /* WITH_DEBUG */
865   str_to_uni(lcaption, caption);
866   MessageBox(g_Wnd, (LPTSTR) lpMsgBuf, lcaption,
867              MB_OK | MB_ICONINFORMATION);
868 #endif /* WITH_DEBUG */
869   LocalFree(lpMsgBuf);
870 }
871 
872 /*****************************************************************************/
873 void
874 mi_paint_rect(char * data, int width, int height, int x, int y, int cx, int cy)
875 {
876   HBITMAP bitmap;
877   BITMAPINFO bi;
878   HDC dc;
879   HDC maindc;
880   HGDIOBJ save;
881   HRGN rgn;
882   void * bits;
883   int i;
884   int j;
885   int colour;
886   int red;
887   int green;
888   int blue;
889   int index;
890 
891   ZeroMemory(&bi, sizeof(bi));
892   bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
893   bi.bmiHeader.biWidth = width;
894   bi.bmiHeader.biHeight = -height;
895   bi.bmiHeader.biPlanes = 1;
896   bi.bmiHeader.biBitCount = 32;
897   bi.bmiHeader.biCompression = BI_RGB;
898   maindc = GetWindowDC(g_Wnd);
899   bitmap = CreateDIBSection(maindc, &bi, DIB_RGB_COLORS, (void **) &bits, 0, 0);
900   if (bitmap == 0)
901   {
902     mi_show_error("CreateDIBSection failed");
903   }
904 
905   if (g_server_depth == 8)
906   {
907     for (i = cy - 1; i >= 0; i--)
908     {
909       for (j = cx - 1; j >= 0; j--)
910       {
911         colour = ((unsigned char*)data)[i * cx + j];
912         red = (pal_entries[colour & 0xff] & 0xff0000) >> 16;
913         green = (pal_entries[colour & 0xff] & 0xff00) >> 8;
914         blue = pal_entries[colour & 0xff] & 0xff;
915         MAKE_COLOUR32(colour, red, green, blue);
916         ((unsigned int*)bits)[i * cx + j] = colour;
917       }
918     }
919   }
920   else if (g_server_depth == 15)
921   {
922     for (i = cy - 1; i >= 0; i--)
923     {
924       for (j = cx - 1; j >= 0; j--)
925       {
926         colour = ((unsigned short*)data)[i * cx + j];
927         SPLIT_COLOUR15(colour, red, green, blue);
928         MAKE_COLOUR32(colour, red, green, blue);
929         ((unsigned int*)bits)[i * cx + j] = colour;
930       }
931     }
932   }
933   else if (g_server_depth == 16)
934   {
935     for (i = cy - 1; i >= 0; i--)
936     {
937       for (j = cx - 1; j >= 0; j--)
938       {
939         colour = ((unsigned short*)data)[i * cx + j];
940         SPLIT_COLOUR16(colour, red, green, blue);
941         MAKE_COLOUR32(colour, red, green, blue);
942         ((unsigned int*)bits)[i * cx + j] = colour;
943       }
944     }
945   }
946   else if (g_server_depth == 24)
947   {
948     for (i = cy - 1; i >= 0; i--)
949     {
950       for (j = cx - 1; j >= 0; j--)
951       {
952         index = (i * cx + j) * 3;
953         red = ((unsigned char*)data)[index + 2];
954         green = ((unsigned char*)data)[index + 1];
955         blue = ((unsigned char*)data)[index];
956         MAKE_COLOUR32(colour, red, green, blue);
957         ((unsigned int*)bits)[i * cx + j] = colour;
958       }
959     }
960   }
961   else if (g_server_depth == 32)
962   {
963     memcpy(bits, data, cx*cy*4);
964   }
965   dc = CreateCompatibleDC(maindc);
966   if (dc == 0)
967   {
968     mi_show_error("CreateCompatibleDC failed");
969   }
970   save = SelectObject(dc, bitmap);
971   rgn = mi_clip(maindc);
972   BitBlt(maindc, x + g_xoff - g_xscroll, y + g_yoff - g_yscroll, cx, cy, dc,
973          0, 0, SRCCOPY);
974   SelectObject(dc, save);
975   DeleteObject(bitmap);
976   DeleteDC(dc);
977   ReleaseDC(g_Wnd, maindc);
978   DeleteObject(rgn);
979 
980 }
981 
982 static INT
983 GetPortNumber(PCHAR szAddress)
984 {
985     PCHAR szPort;
986     INT iPort = TCP_PORT_RDP;
987 
988     szPort = strtok(szAddress, ":");
989 
990     if (szPort != NULL)
991     {
992         szPort = strtok(NULL, ":");
993 
994         if (szPort != NULL)
995         {
996             iPort = atoi(szPort);
997 
998             if (iPort <= 0 || iPort > 0xFFFF)
999                 iPort = TCP_PORT_RDP;
1000         }
1001     }
1002 
1003     return iPort;
1004 }
1005 
1006 static VOID
1007 SetDomainAndUsername(PCHAR pName)
1008 {
1009     PCHAR pDomain;
1010     PCHAR pUsername;
1011 
1012     strcpy(g_domain, "");
1013     strcpy(g_username, "");
1014 
1015     pDomain = strtok(pName, "\\");
1016 
1017     if(pDomain == NULL)
1018         return;
1019 
1020     pUsername = strtok(NULL, "\\");
1021 
1022     if(pUsername == NULL)
1023     {
1024         strcpy(g_username, pDomain);
1025         return;
1026     }
1027 
1028     strcpy(g_username, pUsername);
1029     strcpy(g_domain, pDomain);
1030     return;
1031 }
1032 
1033 static BOOL
1034 ParseCommandLine(LPWSTR lpCmdLine,
1035                  PRDPSETTINGS pRdpSettings,
1036                  BOOL *bSkipDlg)
1037 {
1038     LPWSTR lpStr = lpCmdLine;
1039     WCHAR szSeps[] = L"/";
1040     LPWSTR lpToken;
1041     BOOL bRet = TRUE;
1042 
1043     *bSkipDlg = TRUE;
1044 
1045     if (*lpCmdLine != L'/')
1046     {
1047         LoadRdpSettingsFromFile(pRdpSettings, lpCmdLine);
1048     }
1049     else
1050     {
1051         /* default to screen size, 16bpp */
1052         SetIntegerToSettings(pRdpSettings, L"session bpp", 16);
1053         SetIntegerToSettings(pRdpSettings, L"desktopwidth", GetSystemMetrics(SM_CXSCREEN));
1054         SetIntegerToSettings(pRdpSettings, L"desktopheight", GetSystemMetrics(SM_CYSCREEN));
1055 
1056         lpToken = wcstok(lpStr, szSeps);
1057         while (lpToken)
1058         {
1059             if (wcsncmp(lpToken, L"edit", 4) == 0)
1060             {
1061                 lpToken += 5;
1062                 LoadRdpSettingsFromFile(pRdpSettings, lpToken);
1063                 *bSkipDlg = FALSE;
1064                 break;
1065             }
1066 
1067             if (*lpToken == L'v')
1068             {
1069                 lpToken += 2;
1070                 SetStringToSettings(pRdpSettings, L"full address", lpToken);
1071             }
1072             else if (*lpToken == L'w')
1073             {
1074                 lpToken += 2;
1075                 SetIntegerToSettings(pRdpSettings, L"desktopwidth", _wtoi(lpToken));
1076             }
1077             else if (*lpToken == L'h')
1078             {
1079                 lpToken += 2;
1080                 SetIntegerToSettings(pRdpSettings, L"desktopheight", _wtoi(lpToken));
1081             }
1082             else if (*lpToken == L'f')
1083             {
1084                 SetIntegerToSettings(pRdpSettings, L"screen mode id", 2);
1085             }
1086 
1087             lpToken = wcstok(NULL, szSeps);
1088         }
1089     }
1090 
1091     return bRet;
1092 }
1093 
1094 /*****************************************************************************/
1095 int WINAPI
1096 wWinMain(HINSTANCE hInstance,
1097          HINSTANCE hPrevInstance,
1098          LPWSTR lpCmdLine,
1099          int nCmdShow)
1100 {
1101     PRDPSETTINGS pRdpSettings;
1102     WSADATA d;
1103     int ret = 1;
1104 
1105     if (WSAStartup(MAKEWORD(2, 0), &d) == 0)
1106     {
1107         pRdpSettings = HeapAlloc(GetProcessHeap(),
1108                                  0,
1109                                  sizeof(RDPSETTINGS));
1110         if (pRdpSettings)
1111         {
1112             pRdpSettings->pSettings = NULL;
1113             pRdpSettings->NumSettings = 0;
1114 
1115             if (InitRdpSettings(pRdpSettings))
1116             {
1117                 BOOL bSkipDlg = FALSE;
1118 
1119                 if (*lpCmdLine)
1120                     ParseCommandLine(lpCmdLine, pRdpSettings,&bSkipDlg);
1121                 else
1122                     LoadRdpSettingsFromFile(pRdpSettings, NULL);
1123 
1124                 if (bSkipDlg || OpenRDPConnectDialog(hInstance,
1125                                                       pRdpSettings))
1126                 {
1127                     char szValue[MAXVALUE];
1128                     DWORD dwSize = MAXVALUE;
1129 
1130                     uni_to_str(szValue, GetStringFromSettings(pRdpSettings, L"full address"));
1131 
1132                     /* GetPortNumber also removes possible trailing port number from address */
1133                     g_tcp_port_rdp = GetPortNumber(szValue);
1134                     strcpy(g_servername, szValue);
1135                     uni_to_str(szValue, GetStringFromSettings(pRdpSettings, L"username"));
1136                     SetDomainAndUsername(szValue);
1137                     strcpy(g_password, "");
1138                     if (GetComputerNameA(szValue, &dwSize))
1139                         strcpy(g_hostname, szValue);
1140                     else
1141                         strcpy(g_hostname, tcp_get_address());
1142                     g_server_depth = GetIntegerFromSettings(pRdpSettings, L"session bpp");
1143                     g_screen_width = GetSystemMetrics(SM_CXSCREEN);
1144                     g_screen_height = GetSystemMetrics(SM_CYSCREEN);
1145                     g_width = GetIntegerFromSettings(pRdpSettings, L"desktopwidth");
1146                     g_height = GetIntegerFromSettings(pRdpSettings, L"desktopheight");
1147                     if (GetIntegerFromSettings(pRdpSettings, L"screen mode id") == 2)
1148                     {
1149                         g_fullscreen = 1;
1150                         g_xoff = 0;
1151                         g_yoff = 0;
1152                     }
1153                     else
1154                     {
1155                         g_xoff = GetSystemMetrics(SM_CXEDGE) * 2;
1156                         g_yoff = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYEDGE) * 2;
1157                     }
1158 
1159                     ui_main();
1160                     ret = 0;
1161                 }
1162 
1163                 HeapFree(GetProcessHeap(),
1164                          0,
1165                          pRdpSettings->pSettings);
1166             }
1167 
1168             HeapFree(GetProcessHeap(),
1169                      0,
1170                      pRdpSettings);
1171       }
1172 
1173         WSACleanup();
1174     }
1175 
1176   return ret;
1177 }
1178 
1179 
1180 /*****************************************************************************/
1181 void
1182 mi_begin_update(void)
1183 {
1184 }
1185 
1186 /*****************************************************************************/
1187 void
1188 mi_end_update(void)
1189 {
1190 }
1191 
1192 /*****************************************************************************/
1193 void
1194 mi_fill_rect(int x, int y, int cx, int cy, int colour)
1195 {
1196   HBRUSH brush;
1197   RECT rect;
1198   HDC maindc;
1199   HRGN rgn;
1200   int red;
1201   int green;
1202   int blue;
1203 
1204   if (g_server_depth == 8)
1205   {
1206     red = (pal_entries[colour & 0xff] & 0xff0000) >> 16;
1207     green = (pal_entries[colour & 0xff] & 0xff00) >> 8;
1208     blue = pal_entries[colour & 0xff] & 0xff;
1209   }
1210   else if (g_server_depth == 15)
1211   {
1212     SPLIT_COLOUR15(colour, red, green, blue);
1213   }
1214   else if (g_server_depth == 16)
1215   {
1216     SPLIT_COLOUR16(colour, red, green, blue);
1217   }
1218   else if (g_server_depth == 24 || g_server_depth == 32)
1219   {
1220     red = (colour>>16)&0xff;
1221     green = (colour>>8)&0xff;
1222     blue =  colour&0xff;
1223   }
1224   maindc = GetWindowDC(g_Wnd);
1225   rgn = mi_clip(maindc);
1226   brush = CreateSolidBrush(RGB(red, green, blue));
1227   rect.left = x + g_xoff - g_xscroll;
1228   rect.top = y + g_yoff - g_yscroll;
1229   rect.right = rect.left + cx;
1230   rect.bottom = rect.top + cy;
1231   FillRect(maindc, &rect, brush);
1232   DeleteObject(brush);
1233   ReleaseDC(g_Wnd, maindc);
1234   DeleteObject(rgn);
1235 }
1236 
1237 /*****************************************************************************/
1238 void
1239 mi_line(int x1, int y1, int x2, int y2, int colour)
1240 {
1241   HPEN pen;
1242   HDC maindc;
1243   HGDIOBJ save;
1244   HRGN rgn;
1245   int red;
1246   int green;
1247   int blue;
1248 
1249   if (g_server_depth == 8)
1250   {
1251     red = (pal_entries[colour & 0xff] & 0xff0000) >> 16;
1252     green = (pal_entries[colour & 0xff] & 0xff00) >> 8;
1253     blue = pal_entries[colour & 0xff] & 0xff;
1254   }
1255   else if (g_server_depth == 15)
1256   {
1257     SPLIT_COLOUR15(colour, red, green, blue);
1258   }
1259   else if (g_server_depth == 16)
1260   {
1261     SPLIT_COLOUR16(colour, red, green, blue);
1262   }
1263   else if (g_server_depth == 24 || g_server_depth == 32)
1264   {
1265     red = (colour>>16)&0xff;
1266     green = (colour>>8)&0xff;
1267     blue =  colour&0xff;
1268   }
1269   maindc = GetWindowDC(g_Wnd);
1270   rgn = mi_clip(maindc);
1271   pen = CreatePen(PS_SOLID, 0, RGB(red, green, blue));
1272   save = SelectObject(maindc, pen);
1273   MoveToEx(maindc, x1 + g_xoff - g_xscroll, y1 + g_yoff - g_yscroll, 0);
1274   LineTo(maindc, x2 + g_xoff - g_xscroll, y2 + g_yoff - g_yscroll);
1275   SelectObject(maindc, save);
1276   DeleteObject(pen);
1277   ReleaseDC(g_Wnd, maindc);
1278   DeleteObject(rgn);
1279 }
1280 
1281 /*****************************************************************************/
1282 void
1283 mi_screen_copy(int x, int y, int cx, int cy, int srcx, int srcy)
1284 {
1285   RECT rect;
1286   RECT clip_rect;
1287   RECT draw_rect;
1288   HRGN rgn;
1289   int ok_to_ScrollWindowEx;
1290 
1291   ok_to_ScrollWindowEx = 1;
1292 
1293   if (!ok_to_ScrollWindowEx)
1294   {
1295     rgn = CreateRectRgn(x - g_xscroll, y - g_yscroll,
1296                         (x - g_xscroll) + cx,
1297                         (y - g_yscroll) + cy);
1298     InvalidateRgn(g_Wnd, rgn, 0);
1299     DeleteObject(rgn);
1300   }
1301   else
1302   {
1303     /* this is all in client coords */
1304     rect.left = srcx - g_xscroll;
1305     rect.top = srcy - g_yscroll;
1306     rect.right = rect.left + cx;
1307     rect.bottom = rect.top + cy;
1308     clip_rect.left = g_clip_left - g_xscroll;
1309     clip_rect.top = g_clip_top - g_yscroll;
1310     clip_rect.right = g_clip_right - g_xscroll;
1311     clip_rect.bottom = g_clip_bottom - g_yscroll;
1312     if (IntersectRect(&draw_rect, &clip_rect, &g_wnd_clip))
1313     {
1314       rgn = CreateRectRgn(0, 0, 0, 0);
1315       ScrollWindowEx(g_Wnd, x - srcx, y - srcy, &rect, &draw_rect,
1316                      rgn, 0, SW_ERASE);
1317       InvalidateRgn(g_Wnd, rgn, 0);
1318       DeleteObject(rgn);
1319     }
1320   }
1321 }
1322 
1323 /*****************************************************************************/
1324 void
1325 mi_set_clip(int x, int y, int cx, int cy)
1326 {
1327   g_clip_left = x;
1328   g_clip_top = y;
1329   g_clip_right = g_clip_left + cx;
1330   g_clip_bottom = g_clip_top + cy;
1331 }
1332 
1333 /*****************************************************************************/
1334 void
1335 mi_reset_clip(void)
1336 {
1337   g_clip_left = 0;
1338   g_clip_top = 0;
1339   g_clip_right = g_clip_left + g_width;
1340   g_clip_bottom = g_clip_top + g_height;
1341 }
1342 
1343 /*****************************************************************************/
1344 void *
1345 mi_create_cursor(unsigned int x, unsigned int y,
1346                  int width, int height,
1347                  unsigned char * andmask, unsigned char * xormask)
1348 {
1349   HCURSOR hCur;
1350 
1351   hCur = CreateCursor(g_Instance, x, y, width, height, andmask, xormask);
1352   if (hCur == 0)
1353   {
1354     hCur = LoadCursor(NULL, IDC_ARROW);
1355   }
1356   return hCur;
1357 }
1358 
1359 /*****************************************************************************/
1360 void
1361 mi_destroy_cursor(void * cursor)
1362 {
1363   if (g_cursor == cursor)
1364   {
1365     g_cursor = 0;
1366   }
1367   DestroyCursor(cursor);
1368 }
1369 
1370 /*****************************************************************************/
1371 void
1372 mi_set_cursor(void * cursor)
1373 {
1374   g_cursor = cursor;
1375   SetCursor(g_cursor);
1376 }
1377 
1378 /*****************************************************************************/
1379 void
1380 mi_set_null_cursor(void)
1381 {
1382 }
1383 
1384