src/video/x11/SDL_x11events.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 06 Mar 2002 11:23:08 +0000
changeset 297 f6ffac90895c
parent 252 e8157fcb3114
child 412 ac59b067815b
permissions -rw-r--r--
Updated copyright information for 2002
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 /* Handle the event stream, converting X11 events into SDL events */
    29 
    30 #include <stdio.h>
    31 #include <stdlib.h>
    32 #include <string.h>
    33 #include <setjmp.h>
    34 #include <X11/Xlib.h>
    35 #include <X11/Xutil.h>
    36 #include <X11/keysym.h>
    37 #ifdef __SVR4
    38 #include <X11/Sunkeysym.h>
    39 #endif
    40 #include <sys/types.h>
    41 #include <sys/time.h>
    42 #include <unistd.h>
    43 
    44 #include "SDL.h"
    45 #include "SDL_syswm.h"
    46 #include "SDL_sysevents.h"
    47 #include "SDL_sysvideo.h"
    48 #include "SDL_events_c.h"
    49 #include "SDL_x11video.h"
    50 #include "SDL_x11dga_c.h"
    51 #include "SDL_x11modes_c.h"
    52 #include "SDL_x11image_c.h"
    53 #include "SDL_x11gamma_c.h"
    54 #include "SDL_x11wm_c.h"
    55 #include "SDL_x11mouse_c.h"
    56 #include "SDL_x11events_c.h"
    57 
    58 
    59 /* Define this if you want to debug X11 events */
    60 /*#define DEBUG_XEVENTS*/
    61 
    62 /* The translation tables from an X11 keysym to a SDL keysym */
    63 static SDLKey ODD_keymap[256];
    64 static SDLKey MISC_keymap[256];
    65 SDL_keysym *X11_TranslateKey(Display *display, XKeyEvent *xkey, KeyCode kc,
    66 			     SDL_keysym *keysym);
    67 
    68 /* Check to see if this is a repeated key.
    69    (idea shamelessly lifted from GII -- thanks guys! :)
    70  */
    71 static int X11_KeyRepeat(Display *display, XEvent *event)
    72 {
    73 	XEvent peekevent;
    74 	int repeated;
    75 
    76 	repeated = 0;
    77 	if ( XPending(display) ) {
    78 		XPeekEvent(display, &peekevent);
    79 		if ( (peekevent.type == KeyPress) &&
    80 		     (peekevent.xkey.keycode == event->xkey.keycode) &&
    81 		     ((peekevent.xkey.time-event->xkey.time) < 2) ) {
    82 			repeated = 1;
    83 			XNextEvent(display, &peekevent);
    84 		}
    85 	}
    86 	return(repeated);
    87 }
    88 
    89 /* Note:  The X server buffers and accumulates mouse motion events, so
    90    the motion event generated by the warp may not appear exactly as we
    91    expect it to.  We work around this (and improve performance) by only
    92    warping the pointer when it reaches the edge, and then wait for it.
    93 */
    94 #define MOUSE_FUDGE_FACTOR	8
    95 
    96 static __inline__ int X11_WarpedMotion(_THIS, XEvent *xevent)
    97 {
    98 	int w, h, i;
    99 	int deltax, deltay;
   100 	int posted;
   101 
   102 	w = SDL_VideoSurface->w;
   103 	h = SDL_VideoSurface->h;
   104 	deltax = xevent->xmotion.x - mouse_last.x;
   105 	deltay = xevent->xmotion.y - mouse_last.y;
   106 #ifdef DEBUG_MOTION
   107   printf("Warped mouse motion: %d,%d\n", deltax, deltay);
   108 #endif
   109 	mouse_last.x = xevent->xmotion.x;
   110 	mouse_last.y = xevent->xmotion.y;
   111 	posted = SDL_PrivateMouseMotion(0, 1, deltax, deltay);
   112 
   113 	if ( (xevent->xmotion.x < MOUSE_FUDGE_FACTOR) ||
   114 	     (xevent->xmotion.x > (w-MOUSE_FUDGE_FACTOR)) ||
   115 	     (xevent->xmotion.y < MOUSE_FUDGE_FACTOR) ||
   116 	     (xevent->xmotion.y > (h-MOUSE_FUDGE_FACTOR)) ) {
   117 		/* Get the events that have accumulated */
   118 		while ( XCheckTypedEvent(SDL_Display, MotionNotify, xevent) ) {
   119 			deltax = xevent->xmotion.x - mouse_last.x;
   120 			deltay = xevent->xmotion.y - mouse_last.y;
   121 #ifdef DEBUG_MOTION
   122   printf("Extra mouse motion: %d,%d\n", deltax, deltay);
   123 #endif
   124 			mouse_last.x = xevent->xmotion.x;
   125 			mouse_last.y = xevent->xmotion.y;
   126 			posted += SDL_PrivateMouseMotion(0, 1, deltax, deltay);
   127 		}
   128 		mouse_last.x = w/2;
   129 		mouse_last.y = h/2;
   130 		XWarpPointer(SDL_Display, None, SDL_Window, 0, 0, 0, 0,
   131 					mouse_last.x, mouse_last.y);
   132 		for ( i=0; i<10; ++i ) {
   133         		XMaskEvent(SDL_Display, PointerMotionMask, xevent);
   134 			if ( (xevent->xmotion.x >
   135 			          (mouse_last.x-MOUSE_FUDGE_FACTOR)) &&
   136 			     (xevent->xmotion.x <
   137 			          (mouse_last.x+MOUSE_FUDGE_FACTOR)) &&
   138 			     (xevent->xmotion.y >
   139 			          (mouse_last.y-MOUSE_FUDGE_FACTOR)) &&
   140 			     (xevent->xmotion.y <
   141 			          (mouse_last.y+MOUSE_FUDGE_FACTOR)) ) {
   142 				break;
   143 			}
   144 #ifdef DEBUG_XEVENTS
   145   printf("Lost mouse motion: %d,%d\n", xevent->xmotion.x, xevent->xmotion.y);
   146 #endif
   147 		}
   148 #ifdef DEBUG_XEVENTS
   149 		if ( i == 10 ) {
   150 			printf("Warning: didn't detect mouse warp motion\n");
   151 		}
   152 #endif
   153 	}
   154 	return(posted);
   155 }
   156 
   157 static int X11_DispatchEvent(_THIS)
   158 {
   159 	int posted;
   160 	XEvent xevent;
   161 
   162 	XNextEvent(SDL_Display, &xevent);
   163 
   164 	posted = 0;
   165 	switch (xevent.type) {
   166 
   167 	    /* Gaining mouse coverage? */
   168 	    case EnterNotify: {
   169 #ifdef DEBUG_XEVENTS
   170 printf("EnterNotify! (%d,%d)\n", xevent.xcrossing.x, xevent.xcrossing.y);
   171 if ( xevent.xcrossing.mode == NotifyGrab )
   172 printf("Mode: NotifyGrab\n");
   173 if ( xevent.xcrossing.mode == NotifyUngrab )
   174 printf("Mode: NotifyUngrab\n");
   175 #endif
   176 		if ( (xevent.xcrossing.mode != NotifyGrab) &&
   177 		     (xevent.xcrossing.mode != NotifyUngrab) ) {
   178 			if ( this->input_grab == SDL_GRAB_OFF ) {
   179 				posted = SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
   180 			} else {
   181 				posted = SDL_PrivateMouseMotion(0, 0,
   182 						xevent.xcrossing.x,
   183 						xevent.xcrossing.y);
   184 			}
   185 		}
   186 	    }
   187 	    break;
   188 
   189 	    /* Losing mouse coverage? */
   190 	    case LeaveNotify: {
   191 #ifdef DEBUG_XEVENTS
   192 printf("LeaveNotify! (%d,%d)\n", xevent.xcrossing.x, xevent.xcrossing.y);
   193 if ( xevent.xcrossing.mode == NotifyGrab )
   194 printf("Mode: NotifyGrab\n");
   195 if ( xevent.xcrossing.mode == NotifyUngrab )
   196 printf("Mode: NotifyUngrab\n");
   197 #endif
   198 		if ( (xevent.xcrossing.mode != NotifyGrab) &&
   199 		     (xevent.xcrossing.mode != NotifyUngrab) ) {
   200 			if ( this->input_grab == SDL_GRAB_OFF ) {
   201 				posted = SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
   202 			} else {
   203 				posted = SDL_PrivateMouseMotion(0, 0,
   204 						xevent.xcrossing.x,
   205 						xevent.xcrossing.y);
   206 			}
   207 		}
   208 	    }
   209 	    break;
   210 
   211 	    /* Gaining input focus? */
   212 	    case FocusIn: {
   213 #ifdef DEBUG_XEVENTS
   214 printf("FocusIn!\n");
   215 #endif
   216 		posted = SDL_PrivateAppActive(1, SDL_APPINPUTFOCUS);
   217 
   218 		/* Queue entry into fullscreen mode */
   219 		switch_waiting = 0x01 | SDL_FULLSCREEN;
   220 		switch_time = SDL_GetTicks() + 1500;
   221 	    }
   222 	    break;
   223 
   224 	    /* Losing input focus? */
   225 	    case FocusOut: {
   226 #ifdef DEBUG_XEVENTS
   227 printf("FocusOut!\n");
   228 #endif
   229 		posted = SDL_PrivateAppActive(0, SDL_APPINPUTFOCUS);
   230 
   231 		/* Queue leaving fullscreen mode */
   232 		switch_waiting = 0x01;
   233 		switch_time = SDL_GetTicks() + 200;
   234 	    }
   235 	    break;
   236 
   237 	    /* Generated upon EnterWindow and FocusIn */
   238 	    case KeymapNotify: {
   239 #ifdef DEBUG_XEVENTS
   240 printf("KeymapNotify!\n");
   241 #endif
   242 		X11_SetKeyboardState(SDL_Display, xevent.xkeymap.key_vector);
   243 	    }
   244 	    break;
   245 
   246 	    /* Mouse motion? */
   247 	    case MotionNotify: {
   248 		if ( SDL_VideoSurface ) {
   249 			if ( mouse_relative ) {
   250 				if ( using_dga & DGA_MOUSE ) {
   251 #ifdef DEBUG_MOTION
   252   printf("DGA motion: %d,%d\n", xevent.xmotion.x_root, xevent.xmotion.y_root);
   253 #endif
   254 					posted = SDL_PrivateMouseMotion(0, 1,
   255 							xevent.xmotion.x_root,
   256 							xevent.xmotion.y_root);
   257 				} else {
   258 					posted = X11_WarpedMotion(this,&xevent);
   259 				}
   260 			} else {
   261 #ifdef DEBUG_MOTION
   262   printf("X11 motion: %d,%d\n", xevent.xmotion.x, xevent.xmotion.y);
   263 #endif
   264 				posted = SDL_PrivateMouseMotion(0, 0,
   265 						xevent.xmotion.x,
   266 						xevent.xmotion.y);
   267 			}
   268 		}
   269 	    }
   270 	    break;
   271 
   272 	    /* Mouse button press? */
   273 	    case ButtonPress: {
   274 		posted = SDL_PrivateMouseButton(SDL_PRESSED, 
   275 					xevent.xbutton.button, 0, 0);
   276 	    }
   277 	    break;
   278 
   279 	    /* Mouse button release? */
   280 	    case ButtonRelease: {
   281 		posted = SDL_PrivateMouseButton(SDL_RELEASED, 
   282 					xevent.xbutton.button, 0, 0);
   283 	    }
   284 	    break;
   285 
   286 	    /* Key press? */
   287 	    case KeyPress: {
   288 		SDL_keysym keysym;
   289 
   290 #ifdef DEBUG_XEVENTS
   291 printf("KeyPress (X11 keycode = 0x%X)\n", xevent.xkey.keycode);
   292 #endif
   293 		posted = SDL_PrivateKeyboard(SDL_PRESSED,
   294 				X11_TranslateKey(SDL_Display, &xevent.xkey,
   295 						 xevent.xkey.keycode,
   296 						 &keysym));
   297 	    }
   298 	    break;
   299 
   300 	    /* Key release? */
   301 	    case KeyRelease: {
   302 		SDL_keysym keysym;
   303 
   304 #ifdef DEBUG_XEVENTS
   305 printf("KeyRelease (X11 keycode = 0x%X)\n", xevent.xkey.keycode);
   306 #endif
   307 		/* Check to see if this is a repeated key */
   308 		if ( ! X11_KeyRepeat(SDL_Display, &xevent) ) {
   309 			posted = SDL_PrivateKeyboard(SDL_RELEASED, 
   310 				X11_TranslateKey(SDL_Display, &xevent.xkey,
   311 						 xevent.xkey.keycode,
   312 						 &keysym));
   313 		}
   314 	    }
   315 	    break;
   316 
   317 	    /* Have we been iconified? */
   318 	    case UnmapNotify: {
   319 #ifdef DEBUG_XEVENTS
   320 printf("UnmapNotify!\n");
   321 #endif
   322 		/* If we're active, make ourselves inactive */
   323 		if ( SDL_GetAppState() & SDL_APPACTIVE ) {
   324 			/* Swap out the gamma before we go inactive */
   325 			X11_SwapVidModeGamma(this);
   326 
   327 			/* Send an internal deactivate event */
   328 			posted = SDL_PrivateAppActive(0,
   329 					SDL_APPACTIVE|SDL_APPINPUTFOCUS);
   330 		}
   331 	    }
   332 	    break;
   333 
   334 	    /* Have we been restored? */
   335 	    case MapNotify: {
   336 #ifdef DEBUG_XEVENTS
   337 printf("MapNotify!\n");
   338 #endif
   339 		/* If we're not active, make ourselves active */
   340 		if ( !(SDL_GetAppState() & SDL_APPACTIVE) ) {
   341 			/* Send an internal activate event */
   342 			posted = SDL_PrivateAppActive(1, SDL_APPACTIVE);
   343 
   344 			/* Now that we're active, swap the gamma back */
   345 			X11_SwapVidModeGamma(this);
   346 		}
   347 
   348 		if ( SDL_VideoSurface &&
   349 		     (SDL_VideoSurface->flags & SDL_FULLSCREEN) ) {
   350 			X11_EnterFullScreen(this);
   351 		} else {
   352 			X11_GrabInputNoLock(this, this->input_grab);
   353 		}
   354 		X11_CheckMouseModeNoLock(this);
   355 
   356 		if ( SDL_VideoSurface ) {
   357 			X11_RefreshDisplay(this);
   358 		}
   359 	    }
   360 	    break;
   361 
   362 	    /* Have we been resized or moved? */
   363 	    case ConfigureNotify: {
   364 #ifdef DEBUG_XEVENTS
   365 printf("ConfigureNotify! (resize: %dx%d)\n", xevent.xconfigure.width, xevent.xconfigure.height);
   366 #endif
   367 		if ( SDL_VideoSurface ) {
   368 		    if ((xevent.xconfigure.width != SDL_VideoSurface->w) ||
   369 		        (xevent.xconfigure.height != SDL_VideoSurface->h)) {
   370 			/* FIXME: Find a better fix for the bug with KDE 1.2 */
   371 			if ( ! ((xevent.xconfigure.width == 32) &&
   372 			        (xevent.xconfigure.height == 32)) ) {
   373 				SDL_PrivateResize(xevent.xconfigure.width,
   374 				                  xevent.xconfigure.height);
   375 			}
   376 		    } else {
   377 			/* OpenGL windows need to know about the change */
   378 			if ( SDL_VideoSurface->flags & SDL_OPENGL ) {
   379 				SDL_PrivateExpose();
   380 			}
   381 		    }
   382 		}
   383 	    }
   384 	    break;
   385 
   386 	    /* Have we been requested to quit (or another client message?) */
   387 	    case ClientMessage: {
   388 		if ( (xevent.xclient.format == 32) &&
   389 		     (xevent.xclient.data.l[0] == WM_DELETE_WINDOW) )
   390 		{
   391 			posted = SDL_PrivateQuit();
   392 		} else
   393 		if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) {
   394 			SDL_SysWMmsg wmmsg;
   395 
   396 			SDL_VERSION(&wmmsg.version);
   397 			wmmsg.subsystem = SDL_SYSWM_X11;
   398 			wmmsg.event.xevent = xevent;
   399 			posted = SDL_PrivateSysWMEvent(&wmmsg);
   400 		}
   401 	    }
   402 	    break;
   403 
   404 	    /* Do we need to refresh ourselves? */
   405 	    case Expose: {
   406 #ifdef DEBUG_XEVENTS
   407 printf("Expose (count = %d)\n", xevent.xexpose.count);
   408 #endif
   409 		if ( SDL_VideoSurface && (xevent.xexpose.count == 0) ) {
   410 			X11_RefreshDisplay(this);
   411 		}
   412 	    }
   413 	    break;
   414 
   415 	    default: {
   416 #ifdef DEBUG_XEVENTS
   417 printf("Unhandled event %d\n", xevent.type);
   418 #endif
   419 		/* Only post the event if we're watching for it */
   420 		if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) {
   421 			SDL_SysWMmsg wmmsg;
   422 
   423 			SDL_VERSION(&wmmsg.version);
   424 			wmmsg.subsystem = SDL_SYSWM_X11;
   425 			wmmsg.event.xevent = xevent;
   426 			posted = SDL_PrivateSysWMEvent(&wmmsg);
   427 		}
   428 	    }
   429 	    break;
   430 	}
   431 	return(posted);
   432 }
   433 
   434 /* Ack!  XPending() actually performs a blocking read if no events available */
   435 int X11_Pending(Display *display)
   436 {
   437 	/* Flush the display connection and look to see if events are queued */
   438 	XFlush(display);
   439 	if ( XEventsQueued(display, QueuedAlready) ) {
   440 		return(1);
   441 	}
   442 
   443 	/* More drastic measures are required -- see if X is ready to talk */
   444 	{
   445 		static struct timeval zero_time;	/* static == 0 */
   446 		int x11_fd;
   447 		fd_set fdset;
   448 
   449 		x11_fd = ConnectionNumber(display);
   450 		FD_ZERO(&fdset);
   451 		FD_SET(x11_fd, &fdset);
   452 		if ( select(x11_fd+1, &fdset, NULL, NULL, &zero_time) == 1 ) {
   453 			return(XPending(display));
   454 		}
   455 	}
   456 
   457 	/* Oh well, nothing is ready .. */
   458 	return(0);
   459 }
   460 
   461 void X11_PumpEvents(_THIS)
   462 {
   463 	int pending;
   464 
   465 	/* Keep processing pending events */
   466 	pending = 0;
   467 	while ( X11_Pending(SDL_Display) ) {
   468 		X11_DispatchEvent(this);
   469 		++pending;
   470 	}
   471 	if ( switch_waiting ) {
   472 		Uint32 now;
   473 
   474 		now  = SDL_GetTicks();
   475 		if ( pending || !SDL_VideoSurface ) {
   476 			/* Try again later... */
   477 			if ( switch_waiting & SDL_FULLSCREEN ) {
   478 				switch_time = now + 1500;
   479 			} else {
   480 				switch_time = now + 200;
   481 			}
   482 		} else if ( now >= switch_time ) {
   483 			Uint32 go_fullscreen;
   484 
   485 			go_fullscreen = switch_waiting & SDL_FULLSCREEN;
   486 			switch_waiting = 0;
   487 			if ( SDL_VideoSurface->flags & SDL_FULLSCREEN ) {
   488 				if ( go_fullscreen ) {
   489 					X11_EnterFullScreen(this);
   490 				} else {
   491 					X11_LeaveFullScreen(this);
   492 				}
   493 			}
   494 			/* Handle focus in/out when grabbed */
   495 			if ( go_fullscreen ) {
   496 				X11_GrabInputNoLock(this, this->input_grab);
   497 			} else {
   498 				X11_GrabInputNoLock(this, SDL_GRAB_OFF);
   499 			}
   500 			X11_CheckMouseModeNoLock(this);
   501 		}
   502 	}
   503 }
   504 
   505 void X11_InitKeymap(void)
   506 {
   507 	int i;
   508 
   509 	/* Odd keys used in international keyboards */
   510 	for ( i=0; i<SDL_TABLESIZE(ODD_keymap); ++i )
   511 		ODD_keymap[i] = SDLK_UNKNOWN;
   512 
   513 #ifdef XK_dead_circumflex
   514 	/* These X keysyms have 0xFE as the high byte */
   515 	ODD_keymap[XK_dead_circumflex&0xFF] = SDLK_CARET;
   516 #endif
   517 
   518 	/* Map the miscellaneous keys */
   519 	for ( i=0; i<SDL_TABLESIZE(MISC_keymap); ++i )
   520 		MISC_keymap[i] = SDLK_UNKNOWN;
   521 
   522 	/* These X keysyms have 0xFF as the high byte */
   523 	MISC_keymap[XK_BackSpace&0xFF] = SDLK_BACKSPACE;
   524 	MISC_keymap[XK_Tab&0xFF] = SDLK_TAB;
   525 	MISC_keymap[XK_Clear&0xFF] = SDLK_CLEAR;
   526 	MISC_keymap[XK_Return&0xFF] = SDLK_RETURN;
   527 	MISC_keymap[XK_Pause&0xFF] = SDLK_PAUSE;
   528 	MISC_keymap[XK_Escape&0xFF] = SDLK_ESCAPE;
   529 	MISC_keymap[XK_Delete&0xFF] = SDLK_DELETE;
   530 
   531 	MISC_keymap[XK_KP_0&0xFF] = SDLK_KP0;		/* Keypad 0-9 */
   532 	MISC_keymap[XK_KP_1&0xFF] = SDLK_KP1;
   533 	MISC_keymap[XK_KP_2&0xFF] = SDLK_KP2;
   534 	MISC_keymap[XK_KP_3&0xFF] = SDLK_KP3;
   535 	MISC_keymap[XK_KP_4&0xFF] = SDLK_KP4;
   536 	MISC_keymap[XK_KP_5&0xFF] = SDLK_KP5;
   537 	MISC_keymap[XK_KP_6&0xFF] = SDLK_KP6;
   538 	MISC_keymap[XK_KP_7&0xFF] = SDLK_KP7;
   539 	MISC_keymap[XK_KP_8&0xFF] = SDLK_KP8;
   540 	MISC_keymap[XK_KP_9&0xFF] = SDLK_KP9;
   541 	MISC_keymap[XK_KP_Insert&0xFF] = SDLK_KP0;
   542 	MISC_keymap[XK_KP_End&0xFF] = SDLK_KP1;	
   543 	MISC_keymap[XK_KP_Down&0xFF] = SDLK_KP2;
   544 	MISC_keymap[XK_KP_Page_Down&0xFF] = SDLK_KP3;
   545 	MISC_keymap[XK_KP_Left&0xFF] = SDLK_KP4;
   546 	MISC_keymap[XK_KP_Begin&0xFF] = SDLK_KP5;
   547 	MISC_keymap[XK_KP_Right&0xFF] = SDLK_KP6;
   548 	MISC_keymap[XK_KP_Home&0xFF] = SDLK_KP7;
   549 	MISC_keymap[XK_KP_Up&0xFF] = SDLK_KP8;
   550 	MISC_keymap[XK_KP_Page_Up&0xFF] = SDLK_KP9;
   551 	MISC_keymap[XK_KP_Delete&0xFF] = SDLK_KP_PERIOD;
   552 	MISC_keymap[XK_KP_Decimal&0xFF] = SDLK_KP_PERIOD;
   553 	MISC_keymap[XK_KP_Divide&0xFF] = SDLK_KP_DIVIDE;
   554 	MISC_keymap[XK_KP_Multiply&0xFF] = SDLK_KP_MULTIPLY;
   555 	MISC_keymap[XK_KP_Subtract&0xFF] = SDLK_KP_MINUS;
   556 	MISC_keymap[XK_KP_Add&0xFF] = SDLK_KP_PLUS;
   557 	MISC_keymap[XK_KP_Enter&0xFF] = SDLK_KP_ENTER;
   558 	MISC_keymap[XK_KP_Equal&0xFF] = SDLK_KP_EQUALS;
   559 
   560 	MISC_keymap[XK_Up&0xFF] = SDLK_UP;
   561 	MISC_keymap[XK_Down&0xFF] = SDLK_DOWN;
   562 	MISC_keymap[XK_Right&0xFF] = SDLK_RIGHT;
   563 	MISC_keymap[XK_Left&0xFF] = SDLK_LEFT;
   564 	MISC_keymap[XK_Insert&0xFF] = SDLK_INSERT;
   565 	MISC_keymap[XK_Home&0xFF] = SDLK_HOME;
   566 	MISC_keymap[XK_End&0xFF] = SDLK_END;
   567 	MISC_keymap[XK_Page_Up&0xFF] = SDLK_PAGEUP;
   568 	MISC_keymap[XK_Page_Down&0xFF] = SDLK_PAGEDOWN;
   569 
   570 	MISC_keymap[XK_F1&0xFF] = SDLK_F1;
   571 	MISC_keymap[XK_F2&0xFF] = SDLK_F2;
   572 	MISC_keymap[XK_F3&0xFF] = SDLK_F3;
   573 	MISC_keymap[XK_F4&0xFF] = SDLK_F4;
   574 	MISC_keymap[XK_F5&0xFF] = SDLK_F5;
   575 	MISC_keymap[XK_F6&0xFF] = SDLK_F6;
   576 	MISC_keymap[XK_F7&0xFF] = SDLK_F7;
   577 	MISC_keymap[XK_F8&0xFF] = SDLK_F8;
   578 	MISC_keymap[XK_F9&0xFF] = SDLK_F9;
   579 	MISC_keymap[XK_F10&0xFF] = SDLK_F10;
   580 	MISC_keymap[XK_F11&0xFF] = SDLK_F11;
   581 	MISC_keymap[XK_F12&0xFF] = SDLK_F12;
   582 	MISC_keymap[XK_F13&0xFF] = SDLK_F13;
   583 	MISC_keymap[XK_F14&0xFF] = SDLK_F14;
   584 	MISC_keymap[XK_F15&0xFF] = SDLK_F15;
   585 
   586 	MISC_keymap[XK_Num_Lock&0xFF] = SDLK_NUMLOCK;
   587 	MISC_keymap[XK_Caps_Lock&0xFF] = SDLK_CAPSLOCK;
   588 	MISC_keymap[XK_Scroll_Lock&0xFF] = SDLK_SCROLLOCK;
   589 	MISC_keymap[XK_Shift_R&0xFF] = SDLK_RSHIFT;
   590 	MISC_keymap[XK_Shift_L&0xFF] = SDLK_LSHIFT;
   591 	MISC_keymap[XK_Control_R&0xFF] = SDLK_RCTRL;
   592 	MISC_keymap[XK_Control_L&0xFF] = SDLK_LCTRL;
   593 	MISC_keymap[XK_Alt_R&0xFF] = SDLK_RALT;
   594 	MISC_keymap[XK_Alt_L&0xFF] = SDLK_LALT;
   595 	MISC_keymap[XK_Meta_R&0xFF] = SDLK_RMETA;
   596 	MISC_keymap[XK_Meta_L&0xFF] = SDLK_LMETA;
   597 	MISC_keymap[XK_Super_L&0xFF] = SDLK_LSUPER; /* Left "Windows" */
   598 	MISC_keymap[XK_Super_R&0xFF] = SDLK_RSUPER; /* Right "Windows */
   599 	MISC_keymap[XK_Mode_switch&0xFF] = SDLK_MODE; /* "Alt Gr" key */
   600 	MISC_keymap[XK_Multi_key&0xFF] = SDLK_COMPOSE; /* Multi-key compose */
   601 
   602 	MISC_keymap[XK_Help&0xFF] = SDLK_HELP;
   603 	MISC_keymap[XK_Print&0xFF] = SDLK_PRINT;
   604 	MISC_keymap[XK_Sys_Req&0xFF] = SDLK_SYSREQ;
   605 	MISC_keymap[XK_Break&0xFF] = SDLK_BREAK;
   606 	MISC_keymap[XK_Menu&0xFF] = SDLK_MENU;
   607 	MISC_keymap[XK_Hyper_R&0xFF] = SDLK_MENU;   /* Windows "Menu" key */
   608 }
   609 
   610 SDL_keysym *X11_TranslateKey(Display *display, XKeyEvent *xkey, KeyCode kc,
   611 			     SDL_keysym *keysym)
   612 {
   613 	KeySym xsym;
   614 
   615 	/* Get the raw keyboard scancode */
   616 	keysym->scancode = kc;
   617 	xsym = XKeycodeToKeysym(display, kc, 0);
   618 #ifdef DEBUG_KEYS
   619 	fprintf(stderr, "Translating key 0x%.4x (%d)\n", xsym, kc);
   620 #endif
   621 	/* Get the translated SDL virtual keysym */
   622 	keysym->sym = SDLK_UNKNOWN;
   623 	if ( xsym ) {
   624 		switch (xsym>>8) {
   625 			case 0x1005FF:
   626 #ifdef SunXK_F36
   627 				if ( xsym == SunXK_F36 )
   628 					keysym->sym = SDLK_F11;
   629 #endif
   630 #ifdef SunXK_F37
   631 				if ( xsym == SunXK_F37 )
   632 					keysym->sym = SDLK_F12;
   633 #endif
   634 				break;
   635 			case 0x00:	/* Latin 1 */
   636 			case 0x01:	/* Latin 2 */
   637 			case 0x02:	/* Latin 3 */
   638 			case 0x03:	/* Latin 4 */
   639 			case 0x04:	/* Katakana */
   640 			case 0x05:	/* Arabic */
   641 			case 0x06:	/* Cyrillic */
   642 			case 0x07:	/* Greek */
   643 			case 0x08:	/* Technical */
   644 			case 0x0A:	/* Publishing */
   645 			case 0x0C:	/* Hebrew */
   646 			case 0x0D:	/* Thai */
   647 				keysym->sym = (SDLKey)(xsym&0xFF);
   648 				/* Map capital letter syms to lowercase */
   649 				if ((keysym->sym >= 'A')&&(keysym->sym <= 'Z'))
   650 					keysym->sym += ('a'-'A');
   651 				break;
   652 			case 0xFE:
   653 				keysym->sym = ODD_keymap[xsym&0xFF];
   654 				break;
   655 			case 0xFF:
   656 				keysym->sym = MISC_keymap[xsym&0xFF];
   657 				break;
   658 			default:
   659 				fprintf(stderr,
   660 					"X11: Unknown xsym, sym = 0x%04x\n",
   661 					(unsigned int)xsym);
   662 				break;
   663 		}
   664 	} else {
   665 		/* X11 doesn't know how to translate the key! */
   666 		switch (kc) {
   667 			/* Caution:
   668 			   These keycodes are from the Microsoft Keyboard
   669 			 */
   670 			case 115:
   671 				keysym->sym = SDLK_LSUPER;
   672 				break;
   673 			case 116:
   674 				keysym->sym = SDLK_RSUPER;
   675 				break;
   676 			case 117:
   677 				keysym->sym = SDLK_MENU;
   678 				break;
   679 			default:
   680 				/*
   681 				 * no point in an error message; happens for
   682 				 * several keys when we get a keymap notify
   683 				 */
   684 				break;
   685 		}
   686 	}
   687 	keysym->mod = KMOD_NONE;
   688 
   689 	/* If UNICODE is on, get the UNICODE value for the key */
   690 	keysym->unicode = 0;
   691 	if ( SDL_TranslateUNICODE && xkey ) {
   692 		static XComposeStatus state;
   693 		/* Until we handle the IM protocol, use XLookupString() */
   694 		unsigned char keybuf[32];
   695 
   696 #define BROKEN_XFREE86_INTERNATIONAL_KBD
   697 /* This appears to be a magical flag that is used with AltGr on
   698    international keyboards to signal alternate key translations.
   699    The flag doesn't show up when in fullscreen mode (?)
   700    FIXME:  Check to see if this code is safe for other servers.
   701 */
   702 #ifdef BROKEN_XFREE86_INTERNATIONAL_KBD
   703 		/* Work around what appears to be a bug in XFree86 */
   704 		if ( SDL_GetModState() & KMOD_MODE ) {
   705 			xkey->state |= (1<<13);
   706 		}
   707 #endif
   708 		/* Look up the translated value for the key event */
   709 		if ( XLookupString(xkey, (char *)keybuf, sizeof(keybuf),
   710 							NULL, &state) ) {
   711 			/*
   712 			 * FIXME,: XLookupString() may yield more than one
   713 			 * character, so we need a mechanism to allow for
   714 			 * this (perhaps generate null keypress events with
   715 			 * a unicode value)
   716 			 */
   717 			keysym->unicode = keybuf[0];
   718 		}
   719 	}
   720 	return(keysym);
   721 }
   722 
   723 /* X11 modifier masks for various keys */
   724 static unsigned meta_l_mask, meta_r_mask, alt_l_mask, alt_r_mask;
   725 static unsigned num_mask, mode_switch_mask;
   726 
   727 static void get_modifier_masks(Display *display)
   728 {
   729 	static unsigned got_masks;
   730 	int i, j;
   731 	XModifierKeymap *xmods;
   732 	unsigned n;
   733 
   734 	if(got_masks)
   735 		return;
   736 
   737 	xmods = XGetModifierMapping(display);
   738 	n = xmods->max_keypermod;
   739 	for(i = 3; i < 8; i++) {
   740 		for(j = 0; j < n; j++) {
   741 			KeyCode kc = xmods->modifiermap[i * n + j];
   742 			KeySym ks = XKeycodeToKeysym(display, kc, 0);
   743 			unsigned mask = 1 << i;
   744 			switch(ks) {
   745 			case XK_Num_Lock:
   746 				num_mask = mask; break;
   747 			case XK_Alt_L:
   748 				alt_l_mask = mask; break;
   749 			case XK_Alt_R:
   750 				alt_r_mask = mask; break;
   751 			case XK_Meta_L:
   752 				meta_l_mask = mask; break;
   753 			case XK_Meta_R:
   754 				meta_r_mask = mask; break;
   755 			case XK_Mode_switch:
   756 				mode_switch_mask = mask; break;
   757 			}
   758 		}
   759 	}
   760 	XFreeModifiermap(xmods);
   761 	got_masks = 1;
   762 }
   763 
   764 
   765 /*
   766  * This function is semi-official; it is not officially exported and should
   767  * not be considered part of the SDL API, but may be used by client code
   768  * that *really* needs it (including legacy code).
   769  * It is slow, though, and should be avoided if possible.
   770  *
   771  * Note that it isn't completely accurate either; in particular, multi-key
   772  * sequences (dead accents, compose key sequences) will not work since the
   773  * state has been irrevocably lost.
   774  */
   775 Uint16 X11_KeyToUnicode(SDLKey keysym, SDLMod modifiers)
   776 {
   777 	struct SDL_VideoDevice *this = current_video;
   778 	char keybuf[32];
   779 	int i;
   780 	KeySym xsym = 0;
   781 	XKeyEvent xkey;
   782 	Uint16 unicode;
   783 
   784 	if ( !this || !SDL_Display ) {
   785 		return 0;
   786 	}
   787 
   788 	memset(&xkey, 0, sizeof(xkey));
   789 	xkey.display = SDL_Display;
   790 
   791 	xsym = keysym;		/* last resort if not found */
   792 	for (i = 0; i < 256; ++i) {
   793 		if ( MISC_keymap[i] == keysym ) {
   794 			xsym = 0xFF00 | i;
   795 			break;
   796 		} else if ( ODD_keymap[i] == keysym ) {
   797 			xsym = 0xFE00 | i;
   798 			break;
   799 		}
   800 	}
   801 
   802 	xkey.keycode = XKeysymToKeycode(xkey.display, xsym);
   803 
   804 	get_modifier_masks(SDL_Display);
   805 	if(modifiers & KMOD_SHIFT)
   806 		xkey.state |= ShiftMask;
   807 	if(modifiers & KMOD_CAPS)
   808 		xkey.state |= LockMask;
   809 	if(modifiers & KMOD_CTRL)
   810 		xkey.state |= ControlMask;
   811 	if(modifiers & KMOD_MODE)
   812 		xkey.state |= mode_switch_mask;
   813 	if(modifiers & KMOD_LALT)
   814 		xkey.state |= alt_l_mask;
   815 	if(modifiers & KMOD_RALT)
   816 		xkey.state |= alt_r_mask;
   817 	if(modifiers & KMOD_LMETA)
   818 		xkey.state |= meta_l_mask;
   819 	if(modifiers & KMOD_RMETA)
   820 		xkey.state |= meta_r_mask;
   821 	if(modifiers & KMOD_NUM)
   822 		xkey.state |= num_mask;
   823 
   824 	unicode = 0;
   825 	if ( XLookupString(&xkey, keybuf, sizeof(keybuf), NULL, NULL) )
   826 		unicode = (unsigned char)keybuf[0];
   827 	return(unicode);
   828 }
   829 
   830 /*
   831  * Called when focus is regained, to read the keyboard state and generate
   832  * synthetic keypress/release events.
   833  * key_vec is a bit vector of keycodes (256 bits)
   834  */
   835 void X11_SetKeyboardState(Display *display, const char *key_vec)
   836 {
   837 	char keys_return[32];
   838 	int i, gen_event;
   839 	KeyCode xcode[SDLK_LAST];
   840 	Uint8 new_kstate[SDLK_LAST];
   841 	Uint8 *kstate = SDL_GetKeyState(NULL);
   842 	SDLMod modstate;
   843 	Window junk_window;
   844 	int x, y;
   845 	unsigned int mask;
   846 
   847 	/* The first time the window is mapped, we initialize key state */
   848 	if ( ! key_vec ) {
   849 		key_vec = keys_return;
   850 		XQueryKeymap(display, keys_return);
   851 		gen_event = 0;
   852 	} else {
   853 #if 1 /* We no longer generate key down events, just update state */
   854 		gen_event = 0;
   855 #else
   856 		gen_event = 1;
   857 #endif
   858 	}
   859 
   860 	/* Get the keyboard modifier state */
   861 	modstate = 0;
   862 	get_modifier_masks(display);
   863 	if ( XQueryPointer(display, DefaultRootWindow(display),
   864 		&junk_window, &junk_window, &x, &y, &x, &y, &mask) ) {
   865 		if ( mask & LockMask ) {
   866 			modstate |= KMOD_CAPS;
   867 		}
   868 		if ( mask & mode_switch_mask ) {
   869 			modstate |= KMOD_MODE;
   870 		}
   871 		if ( mask & num_mask ) {
   872 			modstate |= KMOD_NUM;
   873 		}
   874 	}
   875 
   876 	/* Zero the new keyboard state and generate it */
   877 	memset(new_kstate, SDL_RELEASED, sizeof(new_kstate));
   878 	/*
   879 	 * An obvious optimisation is to check entire longwords at a time in
   880 	 * both loops, but we can't be sure the arrays are aligned so it's not
   881 	 * worth the extra complexity
   882 	 */
   883 	for(i = 0; i < 32; i++) {
   884 		int j;
   885 		if(!key_vec[i])
   886 			continue;
   887 		for(j = 0; j < 8; j++) {
   888 			if(key_vec[i] & (1 << j)) {
   889 				SDL_keysym sk;
   890 				KeyCode kc = i << 3 | j;
   891 				X11_TranslateKey(display, NULL, kc, &sk);
   892 				new_kstate[sk.sym] = SDL_PRESSED;
   893 				xcode[sk.sym] = kc;
   894 			}
   895 		}
   896 	}
   897 	for(i = SDLK_FIRST+1; i < SDLK_LAST; i++) {
   898 		int state = new_kstate[i];
   899 
   900 		if ( state == SDL_PRESSED ) {
   901 			switch (i) {
   902 				case SDLK_LSHIFT:
   903 					modstate |= KMOD_LSHIFT;
   904 					break;
   905 				case SDLK_RSHIFT:
   906 					modstate |= KMOD_RSHIFT;
   907 					break;
   908 				case SDLK_LCTRL:
   909 					modstate |= KMOD_LCTRL;
   910 					break;
   911 				case SDLK_RCTRL:
   912 					modstate |= KMOD_RCTRL;
   913 					break;
   914 				case SDLK_LALT:
   915 					modstate |= KMOD_LALT;
   916 					break;
   917 				case SDLK_RALT:
   918 					modstate |= KMOD_RALT;
   919 					break;
   920 				case SDLK_LMETA:
   921 					modstate |= KMOD_LMETA;
   922 					break;
   923 				case SDLK_RMETA:
   924 					modstate |= KMOD_RMETA;
   925 					break;
   926 				default:
   927 					break;
   928 			}
   929 		}
   930 		if ( kstate[i] == state )
   931 			continue;
   932 
   933 		/*
   934 		 * Send a fake keyboard event correcting the difference between
   935 		 * SDL's keyboard state and the actual. Note that there is no
   936 		 * way to find out the scancode for key releases, but since all
   937 		 * keys are released when focus is lost only keypresses should
   938 		 * be sent here
   939 		 */
   940 		if ( gen_event ) {
   941 			SDL_keysym sk;
   942 			memset(&sk, 0, sizeof(sk));
   943 			sk.sym = i;
   944 			sk.scancode = xcode[i];	/* only valid for key press */
   945 			SDL_PrivateKeyboard(state, &sk);
   946 		} else {
   947 			kstate[i] = state;
   948 		}
   949 	}
   950 
   951 	/* Hack - set toggle key state */
   952 	if ( modstate & KMOD_CAPS ) {
   953 		kstate[SDLK_CAPSLOCK] = SDL_PRESSED;
   954 	} else {
   955 		kstate[SDLK_CAPSLOCK] = SDL_RELEASED;
   956 	}
   957 	if ( modstate & KMOD_NUM ) {
   958 		kstate[SDLK_NUMLOCK] = SDL_PRESSED;
   959 	} else {
   960 		kstate[SDLK_NUMLOCK] = SDL_RELEASED;
   961 	}
   962 
   963 	/* Set the final modifier state */
   964 	SDL_SetModState(modstate);
   965 }
   966 
   967 void X11_InitOSKeymap(_THIS)
   968 {
   969 	X11_InitKeymap();
   970 }
   971