src/video/x11/SDL_x11events.c
author Sam Lantinga <slouken@lokigames.com>
Tue, 26 Jun 2001 17:41:16 +0000
changeset 83 4c8b9babaae4
parent 82 2bddc38a9f5d
child 88 71774090f286
permissions -rw-r--r--
Oops, don't include debug code
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001  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@devolution.com
    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 #ifdef GRAB_FULLSCREEN
   351 			X11_EnterFullScreen(this);
   352 #else
   353 			/* Queue entry into fullscreen mode */
   354 			switch_waiting = 0x01 | SDL_FULLSCREEN;
   355 			switch_time = SDL_GetTicks() + 1500;
   356 #endif
   357 		} else {
   358 			X11_GrabInputNoLock(this, this->input_grab);
   359 		}
   360 		X11_CheckMouseModeNoLock(this);
   361 
   362 		if ( SDL_VideoSurface ) {
   363 			X11_RefreshDisplay(this);
   364 		}
   365 	    }
   366 	    break;
   367 
   368 	    /* Have we been resized or moved? */
   369 	    case ConfigureNotify: {
   370 #ifdef DEBUG_XEVENTS
   371 printf("ConfigureNotify! (resize: %dx%d)\n", xevent.xconfigure.width, xevent.xconfigure.height);
   372 #endif
   373 		if ( SDL_VideoSurface ) {
   374 		    if ((xevent.xconfigure.width != SDL_VideoSurface->w) ||
   375 		        (xevent.xconfigure.height != SDL_VideoSurface->h)) {
   376 			/* FIXME: Find a better fix for the bug with KDE 1.2 */
   377 			if ( ! ((xevent.xconfigure.width == 32) &&
   378 			        (xevent.xconfigure.height == 32)) ) {
   379 				SDL_PrivateResize(xevent.xconfigure.width,
   380 				                  xevent.xconfigure.height);
   381 			}
   382 		    } else {
   383 			/* OpenGL windows need to know about the change */
   384 			if ( SDL_VideoSurface->flags & SDL_OPENGL ) {
   385 				SDL_PrivateExpose();
   386 			}
   387 		    }
   388 		}
   389 	    }
   390 	    break;
   391 
   392 	    /* Have we been requested to quit (or another client message?) */
   393 	    case ClientMessage: {
   394 		if ( (xevent.xclient.format == 32) &&
   395 		     (xevent.xclient.data.l[0] == WM_DELETE_WINDOW) )
   396 		{
   397 			posted = SDL_PrivateQuit();
   398 		} else
   399 		if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) {
   400 			SDL_SysWMmsg wmmsg;
   401 
   402 			SDL_VERSION(&wmmsg.version);
   403 			wmmsg.subsystem = SDL_SYSWM_X11;
   404 			wmmsg.event.xevent = xevent;
   405 			posted = SDL_PrivateSysWMEvent(&wmmsg);
   406 		}
   407 	    }
   408 	    break;
   409 
   410 	    /* Do we need to refresh ourselves? */
   411 	    case Expose: {
   412 #ifdef DEBUG_XEVENTS
   413 printf("Expose (count = %d)\n", xevent.xexpose.count);
   414 #endif
   415 		if ( SDL_VideoSurface && (xevent.xexpose.count == 0) ) {
   416 			if ( SDL_VideoSurface->flags & SDL_OPENGL ) {
   417 				SDL_PrivateExpose();
   418 			} else {
   419 				X11_RefreshDisplay(this);
   420 			}
   421 		}
   422 	    }
   423 	    break;
   424 
   425 	    default: {
   426 #ifdef DEBUG_XEVENTS
   427 printf("Unhandled event %d\n", xevent.type);
   428 #endif
   429 		/* Only post the event if we're watching for it */
   430 		if ( SDL_ProcessEvents[SDL_SYSWMEVENT] == SDL_ENABLE ) {
   431 			SDL_SysWMmsg wmmsg;
   432 
   433 			SDL_VERSION(&wmmsg.version);
   434 			wmmsg.subsystem = SDL_SYSWM_X11;
   435 			wmmsg.event.xevent = xevent;
   436 			posted = SDL_PrivateSysWMEvent(&wmmsg);
   437 		}
   438 	    }
   439 	    break;
   440 	}
   441 	return(posted);
   442 }
   443 
   444 /* Ack!  XPending() actually performs a blocking read if no events available */
   445 int X11_Pending(Display *display)
   446 {
   447 	/* Flush the display connection and look to see if events are queued */
   448 	XFlush(display);
   449 	if ( XEventsQueued(display, QueuedAlready) ) {
   450 		return(1);
   451 	}
   452 
   453 	/* More drastic measures are required -- see if X is ready to talk */
   454 	{
   455 		static struct timeval zero_time;	/* static == 0 */
   456 		int x11_fd;
   457 		fd_set fdset;
   458 
   459 		x11_fd = ConnectionNumber(display);
   460 		FD_ZERO(&fdset);
   461 		FD_SET(x11_fd, &fdset);
   462 		if ( select(x11_fd+1, &fdset, NULL, NULL, &zero_time) == 1 ) {
   463 			return(XPending(display));
   464 		}
   465 	}
   466 
   467 	/* Oh well, nothing is ready .. */
   468 	return(0);
   469 }
   470 
   471 void X11_PumpEvents(_THIS)
   472 {
   473 	int pending;
   474 
   475 	/* Keep processing pending events */
   476 	pending = 0;
   477 	while ( X11_Pending(SDL_Display) ) {
   478 		X11_DispatchEvent(this);
   479 		++pending;
   480 	}
   481 	if ( switch_waiting ) {
   482 		Uint32 now;
   483 
   484 		now  = SDL_GetTicks();
   485 		if ( pending || !SDL_VideoSurface ) {
   486 			/* Try again later... */
   487 			if ( switch_waiting & SDL_FULLSCREEN ) {
   488 				switch_time = now + 1500;
   489 			} else {
   490 				switch_time = now + 200;
   491 			}
   492 		} else if ( now >= switch_time ) {
   493 			Uint32 go_fullscreen;
   494 
   495 			go_fullscreen = switch_waiting & SDL_FULLSCREEN;
   496 			switch_waiting = 0;
   497 			if ( SDL_VideoSurface->flags & SDL_FULLSCREEN ) {
   498 				if ( go_fullscreen ) {
   499 					X11_EnterFullScreen(this);
   500 				} else {
   501 					X11_LeaveFullScreen(this);
   502 				}
   503 			}
   504 			/* Handle focus in/out when grabbed */
   505 			if ( go_fullscreen ) {
   506 				X11_GrabInputNoLock(this, this->input_grab);
   507 			} else {
   508 				X11_GrabInputNoLock(this, SDL_GRAB_OFF);
   509 			}
   510 			X11_CheckMouseModeNoLock(this);
   511 		}
   512 	}
   513 }
   514 
   515 void X11_InitKeymap(void)
   516 {
   517 	int i;
   518 
   519 	/* Odd keys used in international keyboards */
   520 	for ( i=0; i<SDL_TABLESIZE(ODD_keymap); ++i )
   521 		ODD_keymap[i] = SDLK_UNKNOWN;
   522 
   523 #ifdef XK_dead_circumflex
   524 	/* These X keysyms have 0xFE as the high byte */
   525 	ODD_keymap[XK_dead_circumflex&0xFF] = SDLK_CARET;
   526 #endif
   527 
   528 	/* Map the miscellaneous keys */
   529 	for ( i=0; i<SDL_TABLESIZE(MISC_keymap); ++i )
   530 		MISC_keymap[i] = SDLK_UNKNOWN;
   531 
   532 	/* These X keysyms have 0xFF as the high byte */
   533 	MISC_keymap[XK_BackSpace&0xFF] = SDLK_BACKSPACE;
   534 	MISC_keymap[XK_Tab&0xFF] = SDLK_TAB;
   535 	MISC_keymap[XK_Clear&0xFF] = SDLK_CLEAR;
   536 	MISC_keymap[XK_Return&0xFF] = SDLK_RETURN;
   537 	MISC_keymap[XK_Pause&0xFF] = SDLK_PAUSE;
   538 	MISC_keymap[XK_Escape&0xFF] = SDLK_ESCAPE;
   539 	MISC_keymap[XK_Delete&0xFF] = SDLK_DELETE;
   540 
   541 	MISC_keymap[XK_KP_0&0xFF] = SDLK_KP0;		/* Keypad 0-9 */
   542 	MISC_keymap[XK_KP_1&0xFF] = SDLK_KP1;
   543 	MISC_keymap[XK_KP_2&0xFF] = SDLK_KP2;
   544 	MISC_keymap[XK_KP_3&0xFF] = SDLK_KP3;
   545 	MISC_keymap[XK_KP_4&0xFF] = SDLK_KP4;
   546 	MISC_keymap[XK_KP_5&0xFF] = SDLK_KP5;
   547 	MISC_keymap[XK_KP_6&0xFF] = SDLK_KP6;
   548 	MISC_keymap[XK_KP_7&0xFF] = SDLK_KP7;
   549 	MISC_keymap[XK_KP_8&0xFF] = SDLK_KP8;
   550 	MISC_keymap[XK_KP_9&0xFF] = SDLK_KP9;
   551 	MISC_keymap[XK_KP_Insert&0xFF] = SDLK_KP0;
   552 	MISC_keymap[XK_KP_End&0xFF] = SDLK_KP1;	
   553 	MISC_keymap[XK_KP_Down&0xFF] = SDLK_KP2;
   554 	MISC_keymap[XK_KP_Page_Down&0xFF] = SDLK_KP3;
   555 	MISC_keymap[XK_KP_Left&0xFF] = SDLK_KP4;
   556 	MISC_keymap[XK_KP_Begin&0xFF] = SDLK_KP5;
   557 	MISC_keymap[XK_KP_Right&0xFF] = SDLK_KP6;
   558 	MISC_keymap[XK_KP_Home&0xFF] = SDLK_KP7;
   559 	MISC_keymap[XK_KP_Up&0xFF] = SDLK_KP8;
   560 	MISC_keymap[XK_KP_Page_Up&0xFF] = SDLK_KP9;
   561 	MISC_keymap[XK_KP_Delete&0xFF] = SDLK_KP_PERIOD;
   562 	MISC_keymap[XK_KP_Decimal&0xFF] = SDLK_KP_PERIOD;
   563 	MISC_keymap[XK_KP_Divide&0xFF] = SDLK_KP_DIVIDE;
   564 	MISC_keymap[XK_KP_Multiply&0xFF] = SDLK_KP_MULTIPLY;
   565 	MISC_keymap[XK_KP_Subtract&0xFF] = SDLK_KP_MINUS;
   566 	MISC_keymap[XK_KP_Add&0xFF] = SDLK_KP_PLUS;
   567 	MISC_keymap[XK_KP_Enter&0xFF] = SDLK_KP_ENTER;
   568 	MISC_keymap[XK_KP_Equal&0xFF] = SDLK_KP_EQUALS;
   569 
   570 	MISC_keymap[XK_Up&0xFF] = SDLK_UP;
   571 	MISC_keymap[XK_Down&0xFF] = SDLK_DOWN;
   572 	MISC_keymap[XK_Right&0xFF] = SDLK_RIGHT;
   573 	MISC_keymap[XK_Left&0xFF] = SDLK_LEFT;
   574 	MISC_keymap[XK_Insert&0xFF] = SDLK_INSERT;
   575 	MISC_keymap[XK_Home&0xFF] = SDLK_HOME;
   576 	MISC_keymap[XK_End&0xFF] = SDLK_END;
   577 	MISC_keymap[XK_Page_Up&0xFF] = SDLK_PAGEUP;
   578 	MISC_keymap[XK_Page_Down&0xFF] = SDLK_PAGEDOWN;
   579 
   580 	MISC_keymap[XK_F1&0xFF] = SDLK_F1;
   581 	MISC_keymap[XK_F2&0xFF] = SDLK_F2;
   582 	MISC_keymap[XK_F3&0xFF] = SDLK_F3;
   583 	MISC_keymap[XK_F4&0xFF] = SDLK_F4;
   584 	MISC_keymap[XK_F5&0xFF] = SDLK_F5;
   585 	MISC_keymap[XK_F6&0xFF] = SDLK_F6;
   586 	MISC_keymap[XK_F7&0xFF] = SDLK_F7;
   587 	MISC_keymap[XK_F8&0xFF] = SDLK_F8;
   588 	MISC_keymap[XK_F9&0xFF] = SDLK_F9;
   589 	MISC_keymap[XK_F10&0xFF] = SDLK_F10;
   590 	MISC_keymap[XK_F11&0xFF] = SDLK_F11;
   591 	MISC_keymap[XK_F12&0xFF] = SDLK_F12;
   592 	MISC_keymap[XK_F13&0xFF] = SDLK_F13;
   593 	MISC_keymap[XK_F14&0xFF] = SDLK_F14;
   594 	MISC_keymap[XK_F15&0xFF] = SDLK_F15;
   595 
   596 	MISC_keymap[XK_Num_Lock&0xFF] = SDLK_NUMLOCK;
   597 	MISC_keymap[XK_Caps_Lock&0xFF] = SDLK_CAPSLOCK;
   598 	MISC_keymap[XK_Scroll_Lock&0xFF] = SDLK_SCROLLOCK;
   599 	MISC_keymap[XK_Shift_R&0xFF] = SDLK_RSHIFT;
   600 	MISC_keymap[XK_Shift_L&0xFF] = SDLK_LSHIFT;
   601 	MISC_keymap[XK_Control_R&0xFF] = SDLK_RCTRL;
   602 	MISC_keymap[XK_Control_L&0xFF] = SDLK_LCTRL;
   603 	MISC_keymap[XK_Alt_R&0xFF] = SDLK_RALT;
   604 	MISC_keymap[XK_Alt_L&0xFF] = SDLK_LALT;
   605 	MISC_keymap[XK_Meta_R&0xFF] = SDLK_RMETA;
   606 	MISC_keymap[XK_Meta_L&0xFF] = SDLK_LMETA;
   607 	MISC_keymap[XK_Super_L&0xFF] = SDLK_LSUPER; /* Left "Windows" */
   608 	MISC_keymap[XK_Super_R&0xFF] = SDLK_RSUPER; /* Right "Windows */
   609 	MISC_keymap[XK_Mode_switch&0xFF] = SDLK_MODE; /* "Alt Gr" key */
   610 	MISC_keymap[XK_Multi_key&0xFF] = SDLK_COMPOSE; /* Multi-key compose */
   611 
   612 	MISC_keymap[XK_Help&0xFF] = SDLK_HELP;
   613 	MISC_keymap[XK_Print&0xFF] = SDLK_PRINT;
   614 	MISC_keymap[XK_Sys_Req&0xFF] = SDLK_SYSREQ;
   615 	MISC_keymap[XK_Break&0xFF] = SDLK_BREAK;
   616 	MISC_keymap[XK_Menu&0xFF] = SDLK_MENU;
   617 	MISC_keymap[XK_Hyper_R&0xFF] = SDLK_MENU;   /* Windows "Menu" key */
   618 }
   619 
   620 SDL_keysym *X11_TranslateKey(Display *display, XKeyEvent *xkey, KeyCode kc,
   621 			     SDL_keysym *keysym)
   622 {
   623 	KeySym xsym;
   624 
   625 	/* Get the raw keyboard scancode */
   626 	keysym->scancode = kc;
   627 	xsym = XKeycodeToKeysym(display, kc, 0);
   628 #ifdef DEBUG_KEYS
   629 	fprintf(stderr, "Translating key 0x%.4x (%d)\n", xsym, kc);
   630 #endif
   631 	/* Get the translated SDL virtual keysym */
   632 	keysym->sym = SDLK_UNKNOWN;
   633 	if ( xsym ) {
   634 		switch (xsym>>8) {
   635 			case 0x1005FF:
   636 #ifdef SunXK_F36
   637 				if ( xsym == SunXK_F36 )
   638 					keysym->sym = SDLK_F11;
   639 #endif
   640 #ifdef SunXK_F37
   641 				if ( xsym == SunXK_F37 )
   642 					keysym->sym = SDLK_F12;
   643 #endif
   644 				break;
   645 			case 0x00:	/* Latin 1 */
   646 			case 0x01:	/* Latin 2 */
   647 			case 0x02:	/* Latin 3 */
   648 			case 0x03:	/* Latin 4 */
   649 			case 0x04:	/* Katakana */
   650 			case 0x05:	/* Arabic */
   651 			case 0x06:	/* Cyrillic */
   652 			case 0x07:	/* Greek */
   653 			case 0x08:	/* Technical */
   654 			case 0x0A:	/* Publishing */
   655 			case 0x0C:	/* Hebrew */
   656 			case 0x0D:	/* Thai */
   657 				keysym->sym = (SDLKey)(xsym&0xFF);
   658 				/* Map capital letter syms to lowercase */
   659 				if ((keysym->sym >= 'A')&&(keysym->sym <= 'Z'))
   660 					keysym->sym += ('a'-'A');
   661 				break;
   662 			case 0xFE:
   663 				keysym->sym = ODD_keymap[xsym&0xFF];
   664 				break;
   665 			case 0xFF:
   666 				keysym->sym = MISC_keymap[xsym&0xFF];
   667 				break;
   668 			default:
   669 				fprintf(stderr,
   670 					"X11: Unknown xsym, sym = 0x%04x\n",
   671 					(unsigned int)xsym);
   672 				break;
   673 		}
   674 	} else {
   675 		/* X11 doesn't know how to translate the key! */
   676 		switch (kc) {
   677 			/* Caution:
   678 			   These keycodes are from the Microsoft Keyboard
   679 			 */
   680 			case 115:
   681 				keysym->sym = SDLK_LSUPER;
   682 				break;
   683 			case 116:
   684 				keysym->sym = SDLK_RSUPER;
   685 				break;
   686 			case 117:
   687 				keysym->sym = SDLK_MENU;
   688 				break;
   689 			default:
   690 				/*
   691 				 * no point in an error message; happens for
   692 				 * several keys when we get a keymap notify
   693 				 */
   694 				break;
   695 		}
   696 	}
   697 	keysym->mod = KMOD_NONE;
   698 
   699 	/* If UNICODE is on, get the UNICODE value for the key */
   700 	keysym->unicode = 0;
   701 	if ( SDL_TranslateUNICODE && xkey ) {
   702 		static XComposeStatus state;
   703 		/* Until we handle the IM protocol, use XLookupString() */
   704 		unsigned char keybuf[32];
   705 
   706 #define BROKEN_XFREE86_INTERNATIONAL_KBD
   707 /* This appears to be a magical flag that is used with AltGr on
   708    international keyboards to signal alternate key translations.
   709    The flag doesn't show up when in fullscreen mode (?)
   710    FIXME:  Check to see if this code is safe for other servers.
   711 */
   712 #ifdef BROKEN_XFREE86_INTERNATIONAL_KBD
   713 		/* Work around what appears to be a bug in XFree86 */
   714 		if ( SDL_GetModState() & KMOD_MODE ) {
   715 			xkey->state |= (1<<13);
   716 		}
   717 #endif
   718 		/* Look up the translated value for the key event */
   719 		if ( XLookupString(xkey, (char *)keybuf, sizeof(keybuf),
   720 							NULL, &state) ) {
   721 			/*
   722 			 * FIXME,: XLookupString() may yield more than one
   723 			 * character, so we need a mechanism to allow for
   724 			 * this (perhaps generate null keypress events with
   725 			 * a unicode value)
   726 			 */
   727 			keysym->unicode = keybuf[0];
   728 		}
   729 	}
   730 	return(keysym);
   731 }
   732 
   733 /* X11 modifier masks for various keys */
   734 static unsigned meta_l_mask, meta_r_mask, alt_l_mask, alt_r_mask;
   735 static unsigned num_mask, mode_switch_mask;
   736 
   737 static void get_modifier_masks(Display *display)
   738 {
   739 	static unsigned got_masks;
   740 	int i, j;
   741 	XModifierKeymap *xmods;
   742 	unsigned n;
   743 
   744 	if(got_masks)
   745 		return;
   746 
   747 	xmods = XGetModifierMapping(display);
   748 	n = xmods->max_keypermod;
   749 	for(i = 3; i < 8; i++) {
   750 		for(j = 0; j < n; j++) {
   751 			KeyCode kc = xmods->modifiermap[i * n + j];
   752 			KeySym ks = XKeycodeToKeysym(display, kc, 0);
   753 			unsigned mask = 1 << i;
   754 			switch(ks) {
   755 			case XK_Num_Lock:
   756 				num_mask = mask; break;
   757 			case XK_Alt_L:
   758 				alt_l_mask = mask; break;
   759 			case XK_Alt_R:
   760 				alt_r_mask = mask; break;
   761 			case XK_Meta_L:
   762 				meta_l_mask = mask; break;
   763 			case XK_Meta_R:
   764 				meta_r_mask = mask; break;
   765 			case XK_Mode_switch:
   766 				mode_switch_mask = mask; break;
   767 			}
   768 		}
   769 	}
   770 	XFreeModifiermap(xmods);
   771 	got_masks = 1;
   772 }
   773 
   774 
   775 /*
   776  * This function is semi-official; it is not officially exported and should
   777  * not be considered part of the SDL API, but may be used by client code
   778  * that *really* needs it (including legacy code).
   779  * It is slow, though, and should be avoided if possible.
   780  *
   781  * Note that it isn't completely accurate either; in particular, multi-key
   782  * sequences (dead accents, compose key sequences) will not work since the
   783  * state has been irrevocably lost.
   784  */
   785 Uint16 X11_KeyToUnicode(SDLKey keysym, SDLMod modifiers)
   786 {
   787 	struct SDL_VideoDevice *this = current_video;
   788 	char keybuf[32];
   789 	int i;
   790 	KeySym xsym = 0;
   791 	XKeyEvent xkey;
   792 	Uint16 unicode;
   793 
   794 	if ( !this || !SDL_Display ) {
   795 		return 0;
   796 	}
   797 
   798 	memset(&xkey, 0, sizeof(xkey));
   799 	xkey.display = SDL_Display;
   800 
   801 	xsym = keysym;		/* last resort if not found */
   802 	for (i = 0; i < 256; ++i) {
   803 		if ( MISC_keymap[i] == keysym ) {
   804 			xsym = 0xFF00 | i;
   805 			break;
   806 		} else if ( ODD_keymap[i] == keysym ) {
   807 			xsym = 0xFE00 | i;
   808 			break;
   809 		}
   810 	}
   811 
   812 	xkey.keycode = XKeysymToKeycode(xkey.display, xsym);
   813 
   814 	get_modifier_masks(SDL_Display);
   815 	if(modifiers & KMOD_SHIFT)
   816 		xkey.state |= ShiftMask;
   817 	if(modifiers & KMOD_CAPS)
   818 		xkey.state |= LockMask;
   819 	if(modifiers & KMOD_CTRL)
   820 		xkey.state |= ControlMask;
   821 	if(modifiers & KMOD_MODE)
   822 		xkey.state |= mode_switch_mask;
   823 	if(modifiers & KMOD_LALT)
   824 		xkey.state |= alt_l_mask;
   825 	if(modifiers & KMOD_RALT)
   826 		xkey.state |= alt_r_mask;
   827 	if(modifiers & KMOD_LMETA)
   828 		xkey.state |= meta_l_mask;
   829 	if(modifiers & KMOD_RMETA)
   830 		xkey.state |= meta_r_mask;
   831 	if(modifiers & KMOD_NUM)
   832 		xkey.state |= num_mask;
   833 
   834 	unicode = 0;
   835 	if ( XLookupString(&xkey, keybuf, sizeof(keybuf), NULL, NULL) )
   836 		unicode = (unsigned char)keybuf[0];
   837 	return(unicode);
   838 }
   839 
   840 /*
   841  * Called when focus is regained, to read the keyboard state and generate
   842  * synthetic keypress/release events.
   843  * key_vec is a bit vector of keycodes (256 bits)
   844  */
   845 void X11_SetKeyboardState(Display *display, const char *key_vec)
   846 {
   847 	char keys_return[32];
   848 	int i, gen_event;
   849 	KeyCode xcode[SDLK_LAST];
   850 	Uint8 new_kstate[SDLK_LAST];
   851 	Uint8 *kstate = SDL_GetKeyState(NULL);
   852 
   853 	/* The first time the window is mapped, we initialize key state */
   854 	if ( ! key_vec ) {
   855 		key_vec = keys_return;
   856 		XQueryKeymap(display, keys_return);
   857 		gen_event = 0;
   858 	} else {
   859 		gen_event = 1;
   860 	}
   861 
   862 	/* Zero the new state and generate it */
   863 	memset(new_kstate, 0, sizeof(new_kstate));
   864 	/*
   865 	 * An obvious optimisation is to check entire longwords at a time in
   866 	 * both loops, but we can't be sure the arrays are aligned so it's not
   867 	 * worth the extra complexity
   868 	 */
   869 	for(i = 0; i < 32; i++) {
   870 		int j;
   871 		if(!key_vec[i])
   872 			continue;
   873 		for(j = 0; j < 8; j++) {
   874 			if(key_vec[i] & (1 << j)) {
   875 				SDL_keysym sk;
   876 				KeyCode kc = i << 3 | j;
   877 				X11_TranslateKey(display, NULL, kc, &sk);
   878 				new_kstate[sk.sym] = 1;
   879 				xcode[sk.sym] = kc;
   880 			}
   881 		}
   882 	}
   883 	for(i = SDLK_FIRST+1; i < SDLK_LAST; i++) {
   884 		int st;
   885 		SDL_keysym sk;
   886 
   887 		if(kstate[i] == new_kstate[i])
   888 			continue;
   889 		/*
   890 		 * Send a fake keyboard event correcting the difference between
   891 		 * SDL's keyboard state and the actual. Note that there is no
   892 		 * way to find out the scancode for key releases, but since all
   893 		 * keys are released when focus is lost only keypresses should
   894 		 * be sent here
   895 		 */
   896 		st = new_kstate[i] ? SDL_PRESSED : SDL_RELEASED;
   897 		memset(&sk, 0, sizeof(sk));
   898 		sk.sym = i;
   899 		sk.scancode = xcode[i];		/* only valid for key press */
   900 		if ( gen_event ) {
   901 			SDL_PrivateKeyboard(st, &sk);
   902 		} else {
   903 			kstate[i] = new_kstate[i];
   904 		}
   905 	}
   906 }
   907 
   908 void X11_InitOSKeymap(_THIS)
   909 {
   910 	X11_InitKeymap();
   911 }
   912