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