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