/* $Id: xevent.c,v 5.5 2001/07/08 09:07:28 bertg Exp $ * * XPilot, a multiplayer gravity war game. Copyright (C) 1991-2001 by * * Bjørn Stabell * Ken Ronny Schouten * Bert Gijsbers * Dick Balaska * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #ifndef _WINDOWS # include # include # include # include # include # include # ifdef __apollo # include # endif #endif #ifdef _WINDOWS # include "NT/winX.h" # include "NT/winAudio.h" # include "NT/winClient.h" # include "NT/winXKey.h" #endif #include "version.h" #include "config.h" #include "const.h" #include "paint.h" #include "xinit.h" #include "keys.h" #include "packet.h" #include "bit.h" #include "setup.h" #include "netclient.h" #include "widget.h" #include "error.h" #include "record.h" #include "portability.h" #include "paintdata.h" #include "talk.h" #include "configure.h" #include "xeventhandlers.h" #include "xevent.h" char xevent_version[] = VERSION; extern char *talk_fast_msgs[]; /* talk macros */ extern setup_t *Setup; static BITV_DECL(keyv, NUM_KEYS); int initialPointerControl = false; bool pointerControl = false; extern Cursor pointerControlCursor; #if defined(JOYSTICK) && defined(__linux__) /* * Joystick support for Linux 1.0 by Eckard Kopatzki (eko@isar.muc.de). * Needs joystick-0.7 by Art Smith, Jeff Tranter, Carlos Puchol. * Which in turn requires Linux 1.0 or higher. */ #include #define JS_DEVICE "/dev/js0" /* * center position of the joystick in X and Y, resp. * thresholds which lead to the emulation of the key action */ #define JS_X0 630 #define JS_Y0 630 #define JS_DX 100 #define JS_DY 100 /* * Functions which are bound to the joystick actions. * These should be specified as defined in default.c. */ #define JS_LEFT KEY_TURN_LEFT #define JS_RIGHT KEY_TURN_RIGHT #define JS_UP KEY_THRUST #define JS_DOWN KEY_SWAP_SETTINGS #define JS_BUTTON0 KEY_FIRE_SHOT #define JS_BUTTON1 KEY_SHIELD static int Key_set(int key, int onoff) { if (onoff) { if (!BITV_ISSET(keyv, key)) { BITV_SET(keyv, key); return true; } } else { if (BITV_ISSET(keyv, key)) { BITV_CLR(keyv, key); return true; } } return false; } static void Joystick_event(void) { static int js_fd = 0; static bool js_avail = false; struct JS_DATA_TYPE js; int change = 0; if (!draw) { return; } if (!js_fd && !js_avail) { if ((js_fd = open(JS_DEVICE, O_RDONLY)) == -1) { return; } js_avail = true; } if (js_avail && read(js_fd, &js, JS_RETURN) == JS_RETURN) { change |= Key_set(JS_BUTTON0, (js.buttons & 1)); change |= Key_set(JS_BUTTON1, (js.buttons & 2)); change |= Key_set(JS_LEFT, (js.x < JS_X0 - JS_DX)); change |= Key_set(JS_RIGHT, (js.x > JS_X0 + JS_DX)); change |= Key_set(JS_UP, (js.y < JS_Y0 - JS_DY)); change |= Key_set(JS_DOWN, (js.y > JS_Y0 + JS_DY)); if (change) { Net_key_change(); } } } #endif keys_t Lookup_key(XEvent *event, KeySym ks, bool reset) { keys_t ret = KEY_DUMMY; static int i = 0; if (reset) { /* binary search since keyDefs is sorted on keysym. */ int lo = 0, hi = maxKeyDefs - 1; while (lo < hi) { i = (lo + hi) >> 1; if (ks > keyDefs[i].keysym) { lo = i + 1; } else { hi = i; } } if (lo == hi && ks == keyDefs[lo].keysym) { while (lo > 0 && ks == keyDefs[lo - 1].keysym) { lo--; } i = lo; ret = keyDefs[i].key; i++; } } else { if (i < maxKeyDefs && ks == keyDefs[i].keysym) { ret = keyDefs[i].key; i++; } } IFWINDOWS( Trace("Lookup_key: got key ks=%04X ret=%d\n", ks, ret); ) #ifdef DEVELOPMENT if (reset && ret == KEY_DUMMY) { static XComposeStatus compose; char str[4]; int count; memset(str, 0, sizeof str); count = XLookupString(&event->xkey, str, 1, &ks, &compose); if (count == NoSymbol) { printf("Unknown keysym: 0x%03lx", ks); } else { printf("No action bound to keysym 0x%03lx", ks); if (*str) { printf(", which is key \"%s\"", str); } } printf("\n"); } #endif return (ret); } void Pointer_control_set_state(int onoff) { if (onoff) { pointerControl = true; XGrabPointer(dpy, draw, true, 0, GrabModeAsync, GrabModeAsync, draw, pointerControlCursor, CurrentTime); XWarpPointer(dpy, None, draw, 0, 0, 0, 0, draw_width/2, draw_height/2); XDefineCursor(dpy, draw, pointerControlCursor); XSelectInput(dpy, draw, PointerMotionMask | ButtonPressMask | ButtonReleaseMask); } else { pointerControl = false; XUngrabPointer(dpy, CurrentTime); XDefineCursor(dpy, draw, None); if (!selectionAndHistory) XSelectInput(dpy, draw, 0); else XSelectInput(dpy, draw, ButtonPressMask | ButtonReleaseMask); } XFlush(dpy); } #ifndef _WINDOWS static void Talk_set_state(bool onoff) { if (onoff) { /* Enable talking, disable pointer control if it is enabled. */ if (pointerControl) { initialPointerControl = true; Pointer_control_set_state(false); } if (selectionAndHistory) { XSelectInput(dpy, draw, PointerMotionMask | ButtonPressMask | ButtonReleaseMask); } Talk_map_window(true); } else { /* Disable talking, enable pointer control if it was enabled. */ Talk_map_window(false); if (initialPointerControl) { initialPointerControl = false; Pointer_control_set_state(true); } } } #else static void Talk_set_state(bool onoff) { char* wintalkstr; if (pointerControl) { initialPointerControl = true; Pointer_control_set_state(false); } wintalkstr = (char*)mfcDoTalkWindow(); if (*wintalkstr) Net_talk(wintalkstr); if (initialPointerControl) { initialPointerControl = false; Pointer_control_set_state(true); } scoresChanged = 1; } #endif int Key_init(void) { if (sizeof(keyv) != KEYBOARD_SIZE) { error ("%s, %d: keyv size %d, KEYBOARD_SIZE is %d", __FILE__, __LINE__, sizeof(keyv), KEYBOARD_SIZE); exit(1); } memset(keyv, 0, sizeof keyv); BITV_SET(keyv, KEY_SHIELD); return 0; } int Key_update(void) { return Send_keyboard(keyv); } bool Key_check_talk_macro(keys_t key) { if (key >= KEY_MSG_1 && key < KEY_MSG_1 + TALK_FAST_NR_OF_MSGS) { /* talk macros */ Talk_macro(talk_fast_msgs[key - KEY_MSG_1]); } return true; } bool Key_press_id_mode(keys_t key) { showRealName = showRealName ? false : true; scoresChanged++; return false; /* server doesn't need to know */ } bool Key_press_autoshield_hack(keys_t key) { if (auto_shield && BITV_ISSET(keyv, KEY_SHIELD)) { BITV_CLR(keyv, KEY_SHIELD); } return false; } bool Key_press_shield(keys_t key) { if (toggle_shield) { shields = !shields; if (shields) { BITV_SET(keyv, key); } else { BITV_CLR(keyv, key); } return true; } else if (auto_shield) { shields = 1; #if 0 shields = 0; BITV_CLR(keyv, key); return true; #endif } return false; } bool Key_press_fuel(keys_t key) { fuelCount = FUEL_NOTIFY; return false; } bool Key_press_swap_settings(keys_t key) { DFLOAT _tmp; #define SWAP(a, b) (_tmp = (a), (a) = (b), (b) = _tmp) SWAP(power, power_s); SWAP(turnspeed, turnspeed_s); SWAP(turnresistance, turnresistance_s); control_count = CONTROL_DELAY; Config_redraw(); return true; } bool Key_press_swap_scalefactor(keys_t key) { #ifdef WINDOWSCALING DFLOAT tmp; tmp = scaleFactor; scaleFactor = scaleFactor_s; scaleFactor_s = tmp; Init_scale_array(); Scale_dashes(); Config_redraw(); #endif return false; } bool Key_press_increase_power(keys_t key) { power = power * 1.10; power = MIN(power, MAX_PLAYER_POWER); Send_power(power); Config_redraw(); control_count = CONTROL_DELAY; return false; /* server doesn't see these keypresses anymore */ } bool Key_press_decrease_power(keys_t key) { power = power * 0.90; power = MAX(power, MIN_PLAYER_POWER); Send_power(power); Config_redraw(); control_count = CONTROL_DELAY; return false; /* server doesn't see these keypresses anymore */ } bool Key_press_increase_turnspeed(keys_t key) { turnspeed = turnspeed * 1.05; turnspeed = MIN(turnspeed, MAX_PLAYER_TURNSPEED); Send_turnspeed(turnspeed); Config_redraw(); control_count = CONTROL_DELAY; return false; /* server doesn't see these keypresses anymore */ } bool Key_press_decrease_turnspeed(keys_t key) { turnspeed = turnspeed * 0.95; turnspeed = MAX(turnspeed, MIN_PLAYER_TURNSPEED); Send_turnspeed(turnspeed); Config_redraw(); control_count = CONTROL_DELAY; return false; /* server doesn't see these keypresses anymore */ } bool Key_press_talk(keys_t key) { Talk_set_state((talk_mapped == false) ? true : false); return false; /* server doesn't need to know */ } bool Key_press_show_items(keys_t key) { TOGGLE_BIT(instruments, SHOW_ITEMS); return false; /* server doesn't need to know */ } bool Key_press_show_messages(keys_t key) { TOGGLE_BIT(instruments, SHOW_MESSAGES); return false; /* server doesn't need to know */ } bool Key_press_pointer_control(keys_t key) { if (version < 0x3202) { error("Cannot use pointer control below version 3.2.3"); } else { Pointer_control_set_state(!pointerControl); } return false; /* server doesn't need to know */ } bool Key_press_toggle_record(keys_t key) { Record_toggle(); return false; /* server doesn't need to know */ } #ifndef _WINDOWS bool Key_press_msgs_stdout(keys_t key) { if (selectionAndHistory) Print_messages_to_stdout(); return false; /* server doesn't need to know */ } #endif bool Key_press_select_lose_item(keys_t key) { if (version < 0x3400) { static int before; if (!before++) { errno = 0; error("Servers less than 3.4.0 dont know how to drop items"); } return false; } if (lose_item_active == 1) { lose_item_active = 2; } else { lose_item_active = 1; } return true; } bool Key_press(keys_t key) { Key_check_talk_macro(key); switch (key) { case KEY_ID_MODE: return (Key_press_id_mode(key)); case KEY_FIRE_SHOT: case KEY_FIRE_LASER: case KEY_FIRE_MISSILE: case KEY_FIRE_TORPEDO: case KEY_FIRE_HEAT: case KEY_DROP_MINE: case KEY_DETACH_MINE: Key_press_autoshield_hack(key); break; case KEY_SHIELD: if (Key_press_shield(key)) return true; break; case KEY_REFUEL: case KEY_REPAIR: case KEY_TANK_NEXT: case KEY_TANK_PREV: Key_press_fuel(key); break; case KEY_SWAP_SETTINGS: if (!Key_press_swap_settings(key)) return false; break; case KEY_SWAP_SCALEFACTOR: if (!Key_press_swap_scalefactor(key)) return false; break; case KEY_INCREASE_POWER: return Key_press_increase_power(key); case KEY_DECREASE_POWER: return Key_press_decrease_power(key); case KEY_INCREASE_TURNSPEED: return Key_press_increase_turnspeed(key); case KEY_DECREASE_TURNSPEED: return Key_press_decrease_turnspeed(key); case KEY_TALK: return Key_press_talk(key); case KEY_TOGGLE_OWNED_ITEMS: return Key_press_show_items(key); case KEY_TOGGLE_MESSAGES: return Key_press_show_messages(key); case KEY_POINTER_CONTROL: return Key_press_pointer_control(key); case KEY_TOGGLE_RECORD: return Key_press_toggle_record(key); #ifndef _WINDOWS case KEY_PRINT_MSGS_STDOUT: return Key_press_msgs_stdout(key); #endif case KEY_SELECT_ITEM: case KEY_LOSE_ITEM: if (!Key_press_select_lose_item(key)) return false; default: break; } if (key < NUM_KEYS) { BITV_SET(keyv, key); } return true; } bool Key_release(keys_t key) { switch (key) { case KEY_ID_MODE: case KEY_TALK: case KEY_TOGGLE_OWNED_ITEMS: case KEY_TOGGLE_MESSAGES: return false; /* server doesn't need to know */ /* Don auto-shield hack */ /* restore shields */ case KEY_FIRE_SHOT: case KEY_FIRE_LASER: case KEY_FIRE_MISSILE: case KEY_FIRE_TORPEDO: case KEY_FIRE_HEAT: case KEY_DROP_MINE: case KEY_DETACH_MINE: if (auto_shield && shields && !BITV_ISSET(keyv, KEY_SHIELD)) { /* Here We need to know if any other weapons are still on */ /* before we turn shield back on */ BITV_CLR(keyv, key); if (!BITV_ISSET(keyv, KEY_FIRE_SHOT) && !BITV_ISSET(keyv, KEY_FIRE_LASER) && !BITV_ISSET(keyv, KEY_FIRE_MISSILE) && !BITV_ISSET(keyv, KEY_FIRE_TORPEDO) && !BITV_ISSET(keyv, KEY_FIRE_HEAT) && !BITV_ISSET(keyv, KEY_DROP_MINE) && !BITV_ISSET(keyv, KEY_DETACH_MINE) ) { BITV_SET(keyv, KEY_SHIELD); } } break; case KEY_SHIELD: if (toggle_shield) { return false; } else if (auto_shield) { shields = 0; #if 0 shields = 1; BITV_SET(keyv, key); return true; #endif } break; case KEY_REFUEL: case KEY_REPAIR: fuelCount = FUEL_NOTIFY; break; case KEY_SELECT_ITEM: case KEY_LOSE_ITEM: if (version < 0x3400) { return false; } if (lose_item_active == 2) { lose_item_active = 1; } else { lose_item_active = -FPS; } break; default: break; } if (key < NUM_KEYS) { BITV_CLR(keyv, key); } return true; } void Key_event(XEvent *event) { KeySym ks; keys_t key; int change = false; bool (*key_do)(keys_t key); switch(event->type) { case KeyPress: key_do = Key_press; break; case KeyRelease: key_do = Key_release; break; default: return; } if ((ks = XLookupKeysym(&event->xkey, 0)) == NoSymbol) { return; } for (key = Lookup_key(event, ks, true); key != KEY_DUMMY; key = Lookup_key(event, ks, false)) { change |= (*key_do)(key); } if (change) { Net_key_change(); } } void Reset_shields(void) { if (toggle_shield || auto_shield) { BITV_SET(keyv, KEY_SHIELD); shields = 1; if (auto_shield) { if (BITV_ISSET(keyv, KEY_FIRE_SHOT) || BITV_ISSET(keyv, KEY_FIRE_LASER) || BITV_ISSET(keyv, KEY_FIRE_MISSILE) || BITV_ISSET(keyv, KEY_FIRE_TORPEDO) || BITV_ISSET(keyv, KEY_FIRE_HEAT) || BITV_ISSET(keyv, KEY_DROP_MINE) || BITV_ISSET(keyv, KEY_DETACH_MINE)) { BITV_CLR(keyv, KEY_SHIELD); } } Net_key_change(); } } void Set_auto_shield(int onoff) { auto_shield = onoff; } void Set_toggle_shield(int onoff) { toggle_shield = onoff; if (toggle_shield) { if (auto_shield) { shields = 1; } else { shields = (BITV_ISSET(keyv, KEY_SHIELD) != 0); } } } void Talk_event(XEvent *event) { if (!Talk_do_event(event)) { Talk_set_state(false); } } int talk_key_repeat_count; XEvent talk_key_repeat_event; void xevent_keyboard(int queued) { #ifndef _WINDOWS int i, n; XEvent event; #endif if (talk_key_repeat_count > 0) { if (++talk_key_repeat_count >= FPS && (talk_key_repeat_count - FPS) % ((FPS + 2) / 3) == 0) { Talk_event(&talk_key_repeat_event); if (!talk_mapped) talk_key_repeat_count = 0; } } #ifndef _WINDOWS if (kdpy) { n = XEventsQueued(kdpy, queued); for (i = 0; i < n; i++) { XNextEvent(kdpy, &event); switch (event.type) { case KeyPress: case KeyRelease: Key_event(&event); break; /* Back in play */ case FocusIn: gotFocus = true; XAutoRepeatOff(kdpy); break; /* Probably not playing now */ case FocusOut: case UnmapNotify: gotFocus = false; XAutoRepeatOn(kdpy); break; case MappingNotify: XRefreshKeyboardMapping(&event.xmapping); break; } } } #endif } ipos delta; ipos mouse; /* position of mouse pointer. */ int movement; /* horizontal mouse movement. */ void xevent_pointer(void) { #ifndef _WINDOWS XEvent event; #endif if (pointerControl) { if (!talk_mapped) { #ifdef _WINDOWS /* This is a HACK to fix mouse control under windows. */ { POINT point; GetCursorPos(&point); movement = point.x - draw_width/2; XWarpPointer(dpy, None, draw, 0, 0, 0, 0, draw_width/2, draw_height/2); } /* fix end */ #endif if (movement != 0) { Send_pointer_move(movement); delta.x = draw_width / 2 - mouse.x; delta.y = draw_height / 2 - mouse.y; if (ABS(delta.x) > 3 * draw_width / 8 || ABS(delta.y) > 1 * draw_height / 8) { #ifndef _WINDOWS memset(&event, 0, sizeof(event)); event.type = MotionNotify; event.xmotion.display = dpy; event.xmotion.window = draw; event.xmotion.x = draw_width/2; event.xmotion.y = draw_height/2; XSendEvent(dpy, draw, False, PointerMotionMask, &event); XWarpPointer(dpy, None, draw, 0, 0, 0, 0, draw_width/2, draw_height/2); #endif XFlush(dpy); } } } } } #ifndef _WINDOWS int x_event(int new_input) #else int win_xevent(XEvent event) #endif { int queued = 0; #ifndef _WINDOWS int i, n; XEvent event; #endif #ifdef SOUND audioEvents(); #endif /* SOUND */ #ifdef JOYSTICK Joystick_event(); #endif /* JOYSTICK */ movement = 0; #ifndef _WINDOWS switch (new_input) { case 0: queued = QueuedAlready; break; case 1: queued = QueuedAfterReading; break; case 2: queued = QueuedAfterFlush; break; default: errno = 0; error("Bad input queue type (%d)", new_input); return -1; } n = XEventsQueued(dpy, queued); for (i = 0; i < n; i++) { XNextEvent(dpy, &event); #endif switch (event.type) { #ifndef _WINDOWS /* * after requesting a selection we are notified that we * can access it. */ case SelectionNotify: SelectionNotify_event(&event); break; /* * we are requested to provide a selection. */ case SelectionRequest: SelectionRequest_event(&event); break; case SelectionClear: if (selectionAndHistory) Clear_selection(); break; case MapNotify: MapNotify_event(&event); break; case ClientMessage: if (ClientMessage_event(&event) == -1) { return -1; } break; /* Back in play */ case FocusIn: FocusIn_event(&event); break; /* Probably not playing now */ case FocusOut: case UnmapNotify: UnmapNotify_event(&event); break; case MappingNotify: XRefreshKeyboardMapping(&event.xmapping); break; case ConfigureNotify: ConfigureNotify_event(&event); break; #endif case KeyPress: talk_key_repeat_count = 0; /* FALLTHROUGH */ case KeyRelease: KeyChanged_event(&event); break; case ButtonPress: ButtonPress_event(&event); break; case MotionNotify: MotionNotify_event(&event); break; case ButtonRelease: if (ButtonRelease_event(&event) == -1) return -1; break; case Expose: Expose_event(&event); break; case EnterNotify: case LeaveNotify: Widget_event(&event); break; default: break; } #ifndef _WINDOWS } #endif xevent_keyboard(queued); xevent_pointer(); return 0; }