1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 //
15 // MOUSELIBW32.CPP
16 //
17 // Library of mouse functions for graphics and text mode
18 //
19 // (c) 1994 Chris Jones
20 // Win32 (allegro) update (c) 1999 Chris Jones
21 //
22 //=============================================================================
23 
24 #if defined (WINDOWS_VERSION)
25 #include <dos.h>
26 #include <conio.h>
27 #include <process.h>
28 #endif
29 
30 #include <stdio.h>
31 
32 #include "util/wgt2allg.h"
33 
34 #ifndef TRUE
35 #define TRUE 1
36 #define FALSE 0
37 #endif
38 
39 #include "ac/gamestate.h"
40 #include "debug/out.h"
41 #include "device/mousew32.h"
42 #include "gfx/bitmap.h"
43 #include "gfx/gfx_util.h"
44 #include "main/graphics_mode.h"
45 #include "platform/base/agsplatformdriver.h"
46 #include "util/math.h"
47 #if defined(MAC_VERSION)
48 #include "ac/global_game.h" // j for IsKeyPressed
49 #endif
50 
51 using namespace AGS::Common;
52 using namespace AGS::Engine;
53 
54 
55 extern char lib_file_name[13];
56 
57 char *mouselibcopyr = "MouseLib32 (c) 1994, 1998 Chris Jones";
58 const int NONE = -1, LEFT = 0, RIGHT = 1, MIDDLE = 2;
59 char currentcursor = 0;
60 // virtual mouse cursor coordinates
61 int mousex = 0, mousey = 0, numcurso = -1, hotx = 0, hoty = 0;
62 // real mouse coordinates and bounds
63 int real_mouse_x = 0, real_mouse_y = 0;
64 int boundx1 = 0, boundx2 = 99999, boundy1 = 0, boundy2 = 99999;
65 int disable_mgetgraphpos = 0;
66 char ignore_bounds = 0;
67 extern char alpha_blend_cursor ;
68 Bitmap *mousecurs[MAXCURSORS];
69 extern color palette[256];
70 extern volatile bool switched_away;
71 
72 namespace Mouse
73 {
74     // Tells whether mouse was locked to the game window
75     bool LockedToWindow = false;
76 
77     // Screen rectangle, in which the mouse movement is controlled by engine
78     Rect  ControlRect;
79     // Mouse control enabled flag
80     bool  ControlEnabled = false;
81     // Flag that tells whether the mouse must be forced to stay inside control rect
82     bool  ConfineInCtrlRect = false;
83     // Mouse speed value provided by user
84     float SpeedVal = 1.f;
85     // Mouse speed unit
86     float SpeedUnit = 1.f;
87     // Actual speed factor (cached)
88     float Speed = 1.f;
89 
90 
91     void AdjustPosition(int &x, int &y);
92 }
93 
mgraphconfine(int x1,int y1,int x2,int y2)94 void mgraphconfine(int x1, int y1, int x2, int y2)
95 {
96   Mouse::ControlRect = Rect(x1, y1, x2, y2);
97   set_mouse_range(Mouse::ControlRect.Left, Mouse::ControlRect.Top, Mouse::ControlRect.Right, Mouse::ControlRect.Bottom);
98   Debug::Printf("Mouse confined: (%d,%d)-(%d,%d) (%dx%d)",
99       Mouse::ControlRect.Left, Mouse::ControlRect.Top, Mouse::ControlRect.Right, Mouse::ControlRect.Bottom,
100       Mouse::ControlRect.GetWidth(), Mouse::ControlRect.GetHeight());
101 }
102 
mgetgraphpos()103 void mgetgraphpos()
104 {
105     poll_mouse();
106     if (disable_mgetgraphpos)
107     {
108         // The cursor coordinates are provided from alternate source;
109         // in this case we completely ignore actual cursor movement.
110         if (!ignore_bounds &&
111             (mousex < boundx1 || mousey < boundy1 || mousex > boundx2 || mousey > boundy2))
112         {
113             mousex = Math::Clamp(boundx1, boundx2, mousex);
114             mousey = Math::Clamp(boundy1, boundy2, mousey);
115             msetgraphpos(mousex, mousey);
116         }
117         return;
118     }
119 
120     if (!switched_away && Mouse::ControlEnabled)
121     {
122         // Control mouse movement by querying mouse mickeys (movement deltas)
123         // and applying them to saved mouse coordinates.
124         int mickey_x, mickey_y;
125         get_mouse_mickeys(&mickey_x, &mickey_y);
126 
127         // Apply mouse speed
128         int dx = Mouse::Speed * mickey_x;
129         int dy = Mouse::Speed * mickey_y;
130 
131         //
132         // Perform actual cursor update
133         //---------------------------------------------------------------------
134         // If the real cursor is inside the control rectangle (read - game window),
135         // then apply sensitivity factors and adjust real cursor position
136         if (Mouse::ControlRect.IsInside(real_mouse_x + dx, real_mouse_y + dy))
137         {
138             real_mouse_x += dx;
139             real_mouse_y += dy;
140             position_mouse(real_mouse_x, real_mouse_y);
141         }
142         // Otherwise, if real cursor was moved outside the control rect, yet we
143         // are required to confine cursor inside one, then adjust cursor position
144         // to stay inside the rect's bounds.
145         else if (Mouse::ConfineInCtrlRect)
146         {
147             real_mouse_x = Math::Clamp(Mouse::ControlRect.Left, Mouse::ControlRect.Right, real_mouse_x + dx);
148             real_mouse_y = Math::Clamp(Mouse::ControlRect.Top, Mouse::ControlRect.Bottom, real_mouse_y + dy);
149             position_mouse(real_mouse_x, real_mouse_y);
150         }
151         // Lastly, if the real cursor is out of the control rect, simply add
152         // actual movement to keep up with the system cursor coordinates.
153         else
154         {
155             real_mouse_x += mickey_x;
156             real_mouse_y += mickey_y;
157         }
158 
159         // Do not update the game cursor if the real cursor is beyond the control rect
160         if (!Mouse::ControlRect.IsInside(real_mouse_x, real_mouse_y))
161             return;
162     }
163     else
164     {
165         // Save real cursor coordinates provided by system
166         real_mouse_x = mouse_x;
167         real_mouse_y = mouse_y;
168     }
169 
170     // Set new in-game cursor position
171     mousex = real_mouse_x;
172     mousey = real_mouse_y;
173 
174     if (!ignore_bounds &&
175         (mousex < boundx1 || mousey < boundy1 || mousex > boundx2 || mousey > boundy2))
176     {
177         mousex = Math::Clamp(boundx1, boundx2, mousex);
178         mousey = Math::Clamp(boundy1, boundy2, mousey);
179         msetgraphpos(mousex, mousey);
180     }
181 
182     // Convert to virtual coordinates
183     Mouse::AdjustPosition(mousex, mousey);
184 }
185 
msetcursorlimit(int x1,int y1,int x2,int y2)186 void msetcursorlimit(int x1, int y1, int x2, int y2)
187 {
188   boundx1 = x1;
189   boundy1 = y1;
190   boundx2 = x2;
191   boundy2 = y2;
192 }
193 
194 int hotxwas = 0, hotywas = 0;
domouse(int str)195 void domouse(int str)
196 {
197   /*
198      TO USE THIS ROUTINE YOU MUST LOAD A MOUSE CURSOR USING mloadcursor.
199      YOU MUST ALSO REMEMBER TO CALL mfreemem AT THE END OF THE PROGRAM.
200   */
201   int poow = mousecurs[currentcursor]->GetWidth();
202   int pooh = mousecurs[currentcursor]->GetHeight();
203   int smx = mousex - hotxwas, smy = mousey - hotywas;
204 
205   mgetgraphpos();
206   mousex -= hotx;
207   mousey -= hoty;
208 
209   if (mousex + poow >= play.viewport.GetWidth())
210     poow = play.viewport.GetWidth() - mousex;
211 
212   if (mousey + pooh >= play.viewport.GetHeight())
213     pooh = play.viewport.GetHeight() - mousey;
214 
215   mousex += hotx;
216   mousey += hoty;
217   hotxwas = hotx;
218   hotywas = hoty;
219 }
220 
ismouseinbox(int lf,int tp,int rt,int bt)221 int ismouseinbox(int lf, int tp, int rt, int bt)
222 {
223   if ((mousex >= lf) & (mousex <= rt) & (mousey >= tp) & (mousey <= bt))
224     return TRUE;
225   else
226     return FALSE;
227 }
228 
mfreemem()229 void mfreemem()
230 {
231   for (int re = 0; re < numcurso; re++) {
232     delete mousecurs[re];
233   }
234 }
235 
mnewcursor(char cursno)236 void mnewcursor(char cursno)
237 {
238   domouse(2);
239   currentcursor = cursno;
240   domouse(1);
241 }
242 
243 
mloadwcursor(char * namm)244 void mloadwcursor(char *namm)
245 {
246   color dummypal[256];
247   if (wloadsprites(&dummypal[0], namm, mousecurs, 0, MAXCURSORS)) {
248     //printf("C_Load_wCursor: Error reading mouse cursor file\n");
249     exit(1);
250   }
251 }
252 
253 int butwas = 0;
mgetbutton()254 int mgetbutton()
255 {
256   int toret = NONE;
257   poll_mouse();
258   int butis = mouse_b;
259 
260   if ((butis > 0) & (butwas > 0))
261     return NONE;  // don't allow holding button down
262 
263   if (butis & 1)
264   {
265     toret = LEFT;
266 #if defined(MAC_VERSION)
267     // j Ctrl-left click should be right-click
268     if (IsKeyPressed(405) || IsKeyPressed(406))
269     {
270       toret = RIGHT;
271     }
272 #endif
273   }
274   else if (butis & 2)
275     toret = RIGHT;
276   else if (butis & 4)
277     toret = MIDDLE;
278 
279   butwas = butis;
280   return toret;
281 }
282 
283 const int MB_ARRAY[3] = { 1, 2, 4 };
misbuttondown(int buno)284 int misbuttondown(int buno)
285 {
286   poll_mouse();
287   if (mouse_b & MB_ARRAY[buno])
288     return TRUE;
289   return FALSE;
290 }
291 
msetgraphpos(int xa,int ya)292 void msetgraphpos(int xa, int ya)
293 {
294   real_mouse_x = xa;
295   real_mouse_y = ya;
296   position_mouse(real_mouse_x, real_mouse_y);
297 }
298 
msethotspot(int xx,int yy)299 void msethotspot(int xx, int yy)
300 {
301   hotx = xx;  // mousex -= hotx; mousey -= hoty;
302   hoty = yy;  // mousex += hotx; mousey += hoty;
303 }
304 
minstalled()305 int minstalled()
306 {
307   return install_mouse();
308 }
309 
AdjustPosition(int & x,int & y)310 void Mouse::AdjustPosition(int &x, int &y)
311 {
312     x = GameScaling.X.UnScalePt(x) - play.viewport.Left;
313     y = GameScaling.Y.UnScalePt(y) - play.viewport.Top;
314 }
315 
SetGraphicArea()316 void Mouse::SetGraphicArea()
317 {
318     Rect dst_r = GameScaling.ScaleRange(play.viewport);
319     mgraphconfine(dst_r.Left, dst_r.Top, dst_r.Right, dst_r.Bottom);
320 }
321 
SetMoveLimit(const Rect & r)322 void Mouse::SetMoveLimit(const Rect &r)
323 {
324     Rect src_r = OffsetRect(r, play.viewport.GetLT());
325     Rect dst_r = GameScaling.ScaleRange(src_r);
326     msetcursorlimit(dst_r.Left, dst_r.Top, dst_r.Right, dst_r.Bottom);
327 }
328 
SetPosition(const Point p)329 void Mouse::SetPosition(const Point p)
330 {
331     msetgraphpos(GameScaling.X.ScalePt(p.X + play.viewport.Left), GameScaling.Y.ScalePt(p.Y + play.viewport.Top));
332 }
333 
IsLockedToWindow()334 bool Mouse::IsLockedToWindow()
335 {
336     return LockedToWindow;
337 }
338 
TryLockToWindow()339 bool Mouse::TryLockToWindow()
340 {
341     if (!LockedToWindow)
342         LockedToWindow = platform->LockMouseToWindow();
343     return LockedToWindow;
344 }
345 
UnlockFromWindow()346 void Mouse::UnlockFromWindow()
347 {
348     platform->UnlockMouse();
349     LockedToWindow = false;
350 }
351 
EnableControl(bool confine)352 void Mouse::EnableControl(bool confine)
353 {
354     ControlEnabled = true;
355     ConfineInCtrlRect = confine;
356 }
357 
DisableControl()358 void Mouse::DisableControl()
359 {
360     ControlEnabled = false;
361     ConfineInCtrlRect = false;
362 }
363 
IsControlEnabled()364 bool Mouse::IsControlEnabled()
365 {
366     return ControlEnabled;
367 }
368 
SetSpeedUnit(float f)369 void Mouse::SetSpeedUnit(float f)
370 {
371     SpeedUnit = f;
372     Speed = SpeedVal / SpeedUnit;
373 }
374 
GetSpeedUnit()375 float Mouse::GetSpeedUnit()
376 {
377     return SpeedUnit;
378 }
379 
SetSpeed(float speed)380 void Mouse::SetSpeed(float speed)
381 {
382     SpeedVal = Math::Max(0.f, speed);
383     Speed = SpeedUnit * SpeedVal;
384 }
385 
GetSpeed()386 float Mouse::GetSpeed()
387 {
388     return SpeedVal;
389 }
390