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