/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #ifdef _WIN32 #include #include #endif #include "io/mouse.h" #include "graphics/2d.h" #include "parse/scripting.h" #define THREADED // to use the proper set of macros #include "osapi/osapi.h" #include "cmdline/cmdline.h" #define MOUSE_MODE_DI 0 #define MOUSE_MODE_WIN 1 LOCAL int Mouse_mode = MOUSE_MODE_WIN; int mouse_inited = 0; #ifdef WIN32 LOCAL int Di_mouse_inited = 0; #endif LOCAL int Mouse_x; LOCAL int Mouse_y; CRITICAL_SECTION mouse_lock; int mouse_flags; int mouse_left_pressed = 0; int mouse_right_pressed = 0; int mouse_middle_pressed = 0; int mouse_left_up = 0; int mouse_right_up = 0; int mouse_middle_up = 0; int Mouse_dx = 0; int Mouse_dy = 0; int Mouse_dz = 0; int Mouse_sensitivity = 4; int Use_mouse_to_fly = 0; int Mouse_hidden = 0; int Keep_mouse_centered = 0; #ifdef WIN32 int di_init(); void di_cleanup(); void mouse_eval_deltas_di(); #endif void mouse_force_pos(int x, int y); static bool Mouse_in_focus = true; void mouse_got_focus() { if ( !mouse_inited ) return; Mouse_in_focus = true; mouse_flush(); } void mouse_lost_focus() { if ( !mouse_inited ) return; Mouse_in_focus = false; mouse_flush(); } int mouse_is_visible() { return !Mouse_hidden; } void mouse_close() { if (!mouse_inited) return; #ifdef WIN32 if (Mouse_mode == MOUSE_MODE_DI) di_cleanup(); #endif mouse_inited = 0; DELETE_CRITICAL_SECTION( mouse_lock ); } void mouse_init() { // Initialize queue if ( mouse_inited ) return; mouse_inited = 1; INITIALIZE_CRITICAL_SECTION( mouse_lock ); ENTER_CRITICAL_SECTION( mouse_lock ); mouse_flags = 0; Mouse_x = gr_screen.max_w / 2; Mouse_y = gr_screen.max_h / 2; #ifdef WIN32 if (Cmdline_no_di_mouse) { Mouse_mode = MOUSE_MODE_WIN; } else { if (!di_init() || Cmdline_window || Cmdline_fullscreen_window) Mouse_mode = MOUSE_MODE_WIN; else Mouse_mode = MOUSE_MODE_DI; } #else Mouse_mode = MOUSE_MODE_WIN; #endif #ifdef SCP_UNIX // we poll for mouse motion events so be sure to skip those in normal event polling SDL_EventState( SDL_MOUSEMOTION, SDL_IGNORE ); // we do want to make sure that button presses go through event polling though // (should be on by default already, just here as a reminder) SDL_EventState( SDL_MOUSEBUTTONDOWN, SDL_ENABLE ); SDL_EventState( SDL_MOUSEBUTTONUP, SDL_ENABLE ); #endif LEAVE_CRITICAL_SECTION( mouse_lock ); atexit( mouse_close ); } // ---------------------------------------------------------------------------- // mouse_mark_button() is called asynchronously by the OS when a mouse button // goes up or down. The mouse button that is affected is passed via the // flags parameter. // // parameters: flags ==> mouse button pressed/released // set ==> 1 - button is pressed // 0 - button is released void mouse_mark_button( uint flags, int set) { if ( !mouse_inited ) return; ENTER_CRITICAL_SECTION( mouse_lock ); if ( !(mouse_flags & MOUSE_LEFT_BUTTON) ) { if ( (flags & MOUSE_LEFT_BUTTON) && (set == 1) ) { mouse_left_pressed++; } } else { if ( (flags & MOUSE_LEFT_BUTTON) && (set == 0) ){ mouse_left_up++; } } if ( !(mouse_flags & MOUSE_RIGHT_BUTTON) ) { if ( (flags & MOUSE_RIGHT_BUTTON) && (set == 1) ){ mouse_right_pressed++; } } else { if ( (flags & MOUSE_RIGHT_BUTTON) && (set == 0) ){ mouse_right_up++; } } if ( !(mouse_flags & MOUSE_MIDDLE_BUTTON) ) { if ( (flags & MOUSE_MIDDLE_BUTTON) && (set == 1) ){ mouse_middle_pressed++; } } else { if ( (flags & MOUSE_MIDDLE_BUTTON) && (set == 0) ){ mouse_middle_up++; } } if ( set ){ mouse_flags |= flags; } else { mouse_flags &= ~flags; } LEAVE_CRITICAL_SECTION(mouse_lock); Script_system.SetHookVar("MouseButton", 'i', &flags); //WMC - On Mouse Pressed and On Mouse Released hooks if (set == 1) { Script_system.RunCondition(CHA_MOUSEPRESSED); } else if (set == 0) { Script_system.RunCondition(CHA_MOUSERELEASED); } Script_system.RemHookVar("MouseButton"); } void mouse_flush() { if (!mouse_inited) return; mouse_eval_deltas(); Mouse_dx = Mouse_dy = Mouse_dz = 0; ENTER_CRITICAL_SECTION( mouse_lock ); mouse_left_pressed = 0; mouse_right_pressed = 0; mouse_middle_pressed = 0; mouse_flags = 0; LEAVE_CRITICAL_SECTION( mouse_lock ); } int mouse_down_count(int n, int reset_count) { int tmp = 0; if ( !mouse_inited ) return 0; if ( (n < LOWEST_MOUSE_BUTTON) || (n > HIGHEST_MOUSE_BUTTON)) return 0; ENTER_CRITICAL_SECTION( mouse_lock ); switch (n) { case MOUSE_LEFT_BUTTON: tmp = mouse_left_pressed; if ( reset_count ) { mouse_left_pressed = 0; } break; case MOUSE_RIGHT_BUTTON: tmp = mouse_right_pressed; if ( reset_count ) { mouse_right_pressed = 0; } break; case MOUSE_MIDDLE_BUTTON: tmp = mouse_middle_pressed; if ( reset_count ) { mouse_middle_pressed = 0; } break; } // end switch LEAVE_CRITICAL_SECTION( mouse_lock ); return tmp; } // mouse_up_count() returns the number of times button n has gone from down to up // since the last call // // parameters: n - button of mouse (see #define's in mouse.h) // int mouse_up_count(int n) { int tmp = 0; if ( !mouse_inited ) return 0; if ( (n < LOWEST_MOUSE_BUTTON) || (n > HIGHEST_MOUSE_BUTTON)) return 0; ENTER_CRITICAL_SECTION( mouse_lock ); switch (n) { case MOUSE_LEFT_BUTTON: tmp = mouse_left_up; mouse_left_up = 0; break; case MOUSE_RIGHT_BUTTON: tmp = mouse_right_up; mouse_right_up = 0; break; case MOUSE_MIDDLE_BUTTON: tmp = mouse_middle_up; mouse_middle_up = 0; break; default: Assert(0); // can't happen break; } // end switch LEAVE_CRITICAL_SECTION( mouse_lock ); return tmp; } // returns 1 if mouse button btn is down, 0 otherwise int mouse_down(int btn) { int tmp; if ( !mouse_inited ) return 0; if ( (btn < LOWEST_MOUSE_BUTTON) || (btn > HIGHEST_MOUSE_BUTTON)) return 0; ENTER_CRITICAL_SECTION( mouse_lock ); if ( mouse_flags & btn ) tmp = 1; else tmp = 0; LEAVE_CRITICAL_SECTION( mouse_lock ); return tmp; } // returns the fraction of time btn has been down since last call // (currently returns 1 if buttons is down, 0 otherwise) // float mouse_down_time(int btn) { float tmp; if ( !mouse_inited ) return 0.0f; if ( (btn < LOWEST_MOUSE_BUTTON) || (btn > HIGHEST_MOUSE_BUTTON)) return 0.0f; ENTER_CRITICAL_SECTION( mouse_lock ); if ( mouse_flags & btn ) tmp = 1.0f; else tmp = 0.0f; LEAVE_CRITICAL_SECTION( mouse_lock ); return tmp; } void mouse_get_delta(int *dx, int *dy, int *dz) { if (dx) *dx = Mouse_dx; if (dy) *dy = Mouse_dy; if (dz) *dz = Mouse_dz; } // Forces the actual windows cursor to be at (x,y). This may be independent of our tracked (x,y) mouse pos. void mouse_force_pos(int x, int y) { if (os_foreground()) { // only mess with windows's mouse if we are in control of it POINT pnt; pnt.x = x; pnt.y = y; setWindowMousePos(&pnt); } } #include "gamesequence/gamesequence.h" // change in mouse position since last call void mouse_eval_deltas() { static int old_x = 0; static int old_y = 0; int tmp_x, tmp_y, cx, cy; Mouse_dx = Mouse_dy = Mouse_dz = 0; if (!mouse_inited) return; #ifdef WIN32 if (Mouse_mode == MOUSE_MODE_DI) { mouse_eval_deltas_di(); return; } #endif cx = gr_screen.max_w / 2; cy = gr_screen.max_h / 2; ENTER_CRITICAL_SECTION( mouse_lock ); POINT pnt; getWindowMousePos(&pnt); tmp_x = pnt.x; tmp_y = pnt.y; Mouse_dx = tmp_x - old_x; Mouse_dy = tmp_y - old_y; Mouse_dz = 0; // Speeds up the menu mouse on higher resolutions. The check for a // visible mouse should eliminate any possible gameplay changes. if ( mouse_is_visible() ) { gr_resize_screen_pos( &Mouse_dx, &Mouse_dy ); } if (Keep_mouse_centered && Mouse_hidden) { if (Mouse_dx || Mouse_dy) mouse_force_pos(cx, cy); old_x = cx; old_y = cy; } else { old_x = tmp_x; old_y = tmp_y; } LEAVE_CRITICAL_SECTION( mouse_lock ); //WMC - For On Mouse Moved trigger if(Mouse_dx != 0 || Mouse_dy != 0) { Script_system.RunCondition(CHA_MOUSEMOVED); } } #ifdef WIN32 #include "directx/vdinput.h" static LPDIRECTINPUT Di_mouse_obj = NULL; static LPDIRECTINPUTDEVICE Di_mouse = NULL; void mouse_eval_deltas_di() { int repeat = 1; HRESULT hr = 0; DIMOUSESTATE mouse_state; Mouse_dx = Mouse_dy = Mouse_dz = 0; if (!Di_mouse_inited) return; repeat = 1; memset(&mouse_state, 0, sizeof(mouse_state)); while (repeat) { repeat = 0; hr = Di_mouse->GetDeviceState(sizeof(mouse_state), &mouse_state); if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) { // DirectInput is telling us that the input stream has // been interrupted. We aren't tracking any state // between polls, so we don't have any special reset // that needs to be done. We just re-acquire and // try again. Sleep(500); // Pause a half second... hr = Di_mouse->Acquire(); if (SUCCEEDED(hr)) repeat = 1; } } if (SUCCEEDED(hr)) { Mouse_dx = (int) mouse_state.lX; Mouse_dy = (int) mouse_state.lY; Mouse_dz = (int) mouse_state.lZ; } else { Mouse_dx = Mouse_dy = Mouse_dz = 0; } // Speeds up the menu mouse on higher resolutions. The check for a // visible mouse should eliminate any possible gameplay changes. if ( mouse_is_visible() ) { gr_resize_screen_pos( &Mouse_dx, &Mouse_dy ); } Mouse_x += Mouse_dx; Mouse_y += Mouse_dy; if (Mouse_x < 0) Mouse_x = 0; if (Mouse_y < 0) Mouse_y = 0; if (Mouse_x >= gr_screen.max_w) Mouse_x = gr_screen.max_w - 1; if (Mouse_y >= gr_screen.max_h) Mouse_y = gr_screen.max_h - 1; // keep the mouse inside our window so we don't switch applications or anything (debug bug people reported?) // JH: Dang! This makes the mouse readings in DirectInput act screwy! // mouse_force_pos(gr_screen.max_w / 2, gr_screen.max_h / 2); } #endif //#ifdef WIN32 int mouse_get_pos(int *xpos, int *ypos) { int flags; if (!Mouse_in_focus) { if (xpos) *xpos = Mouse_x; if (ypos) *ypos = Mouse_y; return 0; } if (Mouse_mode == MOUSE_MODE_DI) { if (xpos) *xpos = Mouse_x; if (ypos) *ypos = Mouse_y; return mouse_flags; } if (!mouse_inited) { *xpos = *ypos = 0; return 0; } POINT pnt; getWindowMousePos(&pnt); // EnterCriticalSection(&mouse_lock); flags = mouse_flags; Mouse_x = pnt.x; Mouse_y = pnt.y; // LeaveCriticalSection(&mouse_lock); if (Mouse_x < 0){ Mouse_x = 0; } if (Mouse_y < 0){ Mouse_y = 0; } if (Mouse_x >= gr_screen.max_w){ Mouse_x = gr_screen.max_w - 1; } if (Mouse_y >= gr_screen.max_h){ Mouse_y = gr_screen.max_h - 1; } if (xpos){ *xpos = Mouse_x; } if (ypos){ *ypos = Mouse_y; } return flags; } int mouse_get_pos_unscaled( int *xpos, int *ypos ) { int flags = mouse_get_pos( xpos, ypos ); gr_unsize_screen_pos( (xpos) ? xpos : NULL, (ypos) ? ypos : NULL ); return flags; } void mouse_get_real_pos(int *mx, int *my) { if (Mouse_mode == MOUSE_MODE_DI) { *mx = Mouse_x; *my = Mouse_y; return; } POINT pnt; getWindowMousePos(&pnt); *mx = pnt.x; *my = pnt.y; } void mouse_set_pos(int xpos, int ypos) { if (Mouse_mode == MOUSE_MODE_DI) { Mouse_x = xpos; Mouse_y = ypos; return; } if ((xpos != Mouse_x) || (ypos != Mouse_y)){ mouse_force_pos(xpos, ypos); } } #ifdef WIN32 int di_init() { HRESULT hr; if (Mouse_mode == MOUSE_MODE_WIN){ return 0; } Di_mouse_inited = 0; hr = DirectInputCreate(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &Di_mouse_obj, NULL); if (FAILED(hr)) { hr = DirectInputCreate(GetModuleHandle(NULL), 0x300, &Di_mouse_obj, NULL); if (FAILED(hr)) { mprintf(( "DirectInputCreate() failed!\n" )); return FALSE; } } hr = Di_mouse_obj->CreateDevice(GUID_SysMouse, &Di_mouse, NULL); if (FAILED(hr)) { mprintf(( "CreateDevice() failed!\n" )); return FALSE; } hr = Di_mouse->SetDataFormat(&c_dfDIMouse); if (FAILED(hr)) { mprintf(( "SetDataFormat() failed!\n" )); return FALSE; } hr = Di_mouse->SetCooperativeLevel((HWND)os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); if (FAILED(hr)) { mprintf(( "SetCooperativeLevel() failed!\n" )); return FALSE; } /* DIPROPDWORD hdr; // Turn on buffering hdr.diph.dwSize = sizeof(DIPROPDWORD); hdr.diph.dwHeaderSize = sizeof(DIPROPHEADER); hdr.diph.dwObj = 0; hdr.diph.dwHow = DIPH_DEVICE; // Apply to entire device hdr.dwData = 16; //MAX_BUFFERED_KEYBOARD_EVENTS; hr = Di_mouse->SetProperty( DIPROP_BUFFERSIZE, &hdr.diph ); if (FAILED(hr)) { mprintf(( "SetProperty DIPROP_BUFFERSIZE failed\n" )); return FALSE; } Di_event = CreateEvent( NULL, FALSE, FALSE, NULL ); Assert(Di_event != NULL); hr = Di_mouse->SetEventNotification(Di_event); if (FAILED(hr)) { mprintf(( "SetEventNotification failed\n" )); return FALSE; } */ Di_mouse->Acquire(); Di_mouse_inited = 1; return TRUE; } void di_cleanup() { // Destroy any lingering IDirectInputDevice object. if (Di_mouse) { // Unacquire the device one last time just in case we got really confused // and tried to exit while the device is still acquired. Di_mouse->Unacquire(); Di_mouse->Release(); Di_mouse = NULL; } // Destroy any lingering IDirectInput object. if (Di_mouse_obj) { Di_mouse_obj->Release(); Di_mouse_obj = NULL; } Di_mouse_inited = 0; } #endif //ifdef WIN32 // portable routine to get the mouse position, relative // to current window void getWindowMousePos(POINT * pt) { Assert(pt != NULL); #ifdef _WIN32 GetCursorPos(pt); ScreenToClient((HWND)os_get_window(), pt); #else SDL_GetMouseState(&pt->x, &pt->y); #endif } // portable routine to get the mouse position, relative // to current window void setWindowMousePos(POINT * pt) { Assert(pt != NULL); #ifdef _WIN32 ClientToScreen((HWND) os_get_window(), pt); SetCursorPos(pt->x, pt->y); #else SDL_WarpMouse(pt->x, pt->y); #endif }