Workaround implemented for passing faked mickeys to mouse callback
[wine] / dlls / winedos / int33.c
1 /*
2  * DOS interrupt 33h handler
3  *
4  * Copyright 1999 Ove Kåven
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "miscemu.h"
29 #include "dosexe.h"
30 #include "vga.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(int);
34
35 static struct
36 {
37   DWORD x, y, but;
38   WORD lbcount, rbcount, rlastx, rlasty, llastx, llasty;
39   FARPROC16 callback;
40   WORD callmask;
41   WORD VMPratio, HMPratio, oldx, oldy;
42 } mouse_info;
43
44 /**********************************************************************
45  *          DOSVM_Int33Handler (WINEDOS16.151)
46  *
47  * Handler for int 33h (MS MOUSE).
48  */
49 void WINAPI DOSVM_Int33Handler( CONTEXT86 *context )
50 {
51   switch (LOWORD(context->Eax)) {
52   case 0x00:
53     TRACE("Reset mouse driver and request status\n");
54     SET_AX( context, 0xFFFF ); /* installed */
55     SET_BX( context, 3 );      /* # of buttons */
56     memset( &mouse_info, 0, sizeof(mouse_info) );
57     /* Set the default mickey/pixel ratio */
58     mouse_info.HMPratio = 8;
59     mouse_info.VMPratio = 16;
60     break;
61   case 0x01:
62     FIXME("Show mouse cursor\n");
63     break;
64   case 0x02:
65     FIXME("Hide mouse cursor\n");
66     break;
67   case 0x03:
68     TRACE("Return mouse position and button status: (%ld,%ld) and %ld\n",
69          mouse_info.x, mouse_info.y, mouse_info.but);
70     SET_BX( context, mouse_info.but );
71     SET_CX( context, mouse_info.x );
72     SET_DX( context, mouse_info.y );
73     break;
74   case 0x04:
75     FIXME("Position mouse cursor\n");
76     break;
77   case 0x05:
78     TRACE("Return Mouse button press Information for %s mouse button\n",
79           BX_reg(context) ? "right" : "left");
80     if (BX_reg(context)) {
81       SET_BX( context, mouse_info.rbcount );
82       mouse_info.rbcount = 0;
83       SET_CX( context, mouse_info.rlastx );
84       SET_DX( context, mouse_info.rlasty );
85     } else {
86       SET_BX( context, mouse_info.lbcount );
87       mouse_info.lbcount = 0;
88       SET_CX( context, mouse_info.llastx );
89       SET_DX( context, mouse_info.llasty );
90     }
91     SET_AX( context, mouse_info.but );
92     break;
93   case 0x07:
94     FIXME("Define horizontal mouse cursor range %d..%d\n",
95           CX_reg(context), DX_reg(context));
96     break;
97   case 0x08:
98     FIXME("Define vertical mouse cursor range %d..%d\n",
99           CX_reg(context), DX_reg(context));
100     break;
101   case 0x09:
102     FIXME("Define graphics mouse cursor\n");
103     break;
104   case 0x0A:
105     FIXME("Define text mouse cursor\n");
106     break;
107   case 0x0B:
108     TRACE("Read Mouse motion counters\n");
109     SET_CX( context, (mouse_info.x - mouse_info.oldx) * (mouse_info.HMPratio / 8) );
110     SET_DX( context, (mouse_info.y - mouse_info.oldy) * (mouse_info.VMPratio / 8) );
111     mouse_info.oldx = mouse_info.x;
112     mouse_info.oldy = mouse_info.y;
113     break;
114   case 0x0C:
115     TRACE("Define mouse interrupt subroutine\n");
116     mouse_info.callmask = CX_reg(context);
117     mouse_info.callback = (FARPROC16)MAKESEGPTR(context->SegEs, LOWORD(context->Edx));
118     break;
119   case 0x0F:
120     TRACE("Set mickey/pixel ratio\n");
121     mouse_info.HMPratio = CX_reg(context);
122     mouse_info.VMPratio = DX_reg(context);
123     break;
124   case 0x10:
125     FIXME("Define screen region for update\n");
126     break;
127   default:
128     INT_BARF(context,0x33);
129   }
130 }
131
132 typedef struct {
133   FARPROC16 proc;
134   WORD mask,but,x,y,mx,my;
135 } MCALLDATA;
136
137 static void MouseRelay(CONTEXT86 *context,void *mdata)
138 {
139   MCALLDATA *data = (MCALLDATA *)mdata;
140   CONTEXT86 ctx = *context;
141
142   if (!ISV86(&ctx))
143   {
144       ctx.EFlags |= V86_FLAG;
145       ctx.SegSs = 0; /* Allocate new stack. */
146   }
147
148   ctx.Eax   = data->mask;
149   ctx.Ebx   = data->but;
150   ctx.Ecx   = data->x;
151   ctx.Edx   = data->y;
152   ctx.Esi   = data->mx;
153   ctx.Edi   = data->my;
154   ctx.SegCs = SELECTOROF(data->proc);
155   ctx.Eip   = OFFSETOF(data->proc);
156   free(data);
157   DPMI_CallRMProc(&ctx, NULL, 0, 0);
158 }
159
160 static void QueueMouseRelay(DWORD mx, DWORD my, WORD mask)
161 {
162   mouse_info.x = mx;
163   mouse_info.y = my;
164
165   /* Left button down */
166   if(mask & 0x02) {
167     mouse_info.but |= 0x01;
168     mouse_info.llastx = mx;
169     mouse_info.llasty = my;
170     mouse_info.lbcount++;
171   }
172
173   /* Left button up */
174   if(mask & 0x04) {
175     mouse_info.but &= ~0x01;
176   }
177
178   /* Right button down */
179   if(mask & 0x08) {
180     mouse_info.but |= 0x02;
181     mouse_info.rlastx = mx;
182     mouse_info.rlasty = my;
183     mouse_info.rbcount++;
184   }
185
186   /* Right button up */
187   if(mask & 0x10) {
188     mouse_info.but &= ~0x02;
189   }
190
191   /* Middle button down */
192   if(mask & 0x20) {
193     mouse_info.but |= 0x04;
194   }
195
196   /* Middle button up */
197   if(mask & 0x40) {
198     mouse_info.but &= ~0x04;
199   }
200
201   if ((mask & mouse_info.callmask) && mouse_info.callback) {
202     MCALLDATA *data = calloc(1,sizeof(MCALLDATA));
203     data->proc = mouse_info.callback;
204     data->mask = mask & mouse_info.callmask;
205     data->but = mouse_info.but;
206     data->x = mouse_info.x;
207     data->y = mouse_info.y;
208
209     /*
210      * Fake mickeys. 
211      *
212      * FIXME: This is not entirely correct. If mouse if moved to the edge
213      *        of the screen, mouse will stop moving and mickeys won't
214      *        be updated even though they should be.
215      */
216     data->mx = mouse_info.x * (mouse_info.HMPratio / 8);
217     data->my = mouse_info.y * (mouse_info.VMPratio / 8);
218
219     DOSVM_QueueEvent(-1, DOS_PRIORITY_MOUSE, MouseRelay, data);
220   }
221 }
222
223 void WINAPI DOSVM_Int33Message(UINT message,WPARAM wParam,LPARAM lParam)
224 {
225   WORD mask = 0;
226   unsigned Height, Width, SX=1, SY=1;
227
228   if (!VGA_GetMode(&Height,&Width,NULL)) {
229     /* may need to do some coordinate scaling */
230     if (Width)
231       SX = 640/Width;
232     if (!SX) SX=1;
233   }
234
235   switch (message) {
236   case WM_MOUSEMOVE:
237     mask |= 0x01;
238     break;
239   case WM_LBUTTONDOWN:
240   case WM_LBUTTONDBLCLK:
241     mask |= 0x02;
242     break;
243   case WM_LBUTTONUP:
244     mask |= 0x04;
245     break;
246   case WM_RBUTTONDOWN:
247   case WM_RBUTTONDBLCLK:
248     mask |= 0x08;
249     break;
250   case WM_RBUTTONUP:
251     mask |= 0x10;
252     break;
253   case WM_MBUTTONDOWN:
254   case WM_MBUTTONDBLCLK:
255     mask |= 0x20;
256     break;
257   case WM_MBUTTONUP:
258     mask |= 0x40;
259     break;
260   }
261
262   QueueMouseRelay(LOWORD(lParam) * SX,
263                  HIWORD(lParam) * SY,
264                  mask);
265 }
266
267 void WINAPI DOSVM_Int33Console(MOUSE_EVENT_RECORD *record)
268 {
269   unsigned Height, Width;
270   WORD mask = 0;
271   BOOL newLeftButton = record->dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED;
272   BOOL oldLeftButton = mouse_info.but & 0x01;
273   BOOL newRightButton = record->dwButtonState & RIGHTMOST_BUTTON_PRESSED;
274   BOOL oldRightButton = mouse_info.but & 0x02;
275   BOOL newMiddleButton = record->dwButtonState & FROM_LEFT_2ND_BUTTON_PRESSED;
276   BOOL oldMiddleButton = mouse_info.but & 0x04;
277
278   if(newLeftButton && !oldLeftButton)
279     mask |= 0x02;
280   else if(!newLeftButton && oldLeftButton)
281     mask |= 0x04;
282
283   if(newRightButton && !oldRightButton)
284     mask |= 0x08;
285   else if(!newRightButton && oldRightButton)
286     mask |= 0x10;
287
288   if(newMiddleButton && !oldMiddleButton)
289     mask |= 0x20;
290   else if(!newMiddleButton && oldMiddleButton)
291     mask |= 0x40;
292   
293   if (VGA_GetAlphaMode(&Width, &Height))
294     QueueMouseRelay( 640 / Width * record->dwMousePosition.X,
295                      200 / Height * record->dwMousePosition.Y,
296                      mask );
297 }