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