DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/gui/sdlmain_linux.cpp
00001 
00002 #include "config.h"
00003 
00004 #ifdef LINUX
00005 #include "logging.h"
00006 #include "keymap.h"
00007 #include "SDL.h"
00008 #include "SDL_version.h"
00009 #include "SDL_syswm.h"
00010 
00011 #include <X11/XKBlib.h>
00012 #include <X11/extensions/XKBrules.h>
00013 
00014 void UpdateWindowDimensions(Bitu width, Bitu height);
00015 void UpdateWindowMaximized(bool flag);
00016 
00017 #if !defined(C_SDL2)
00018 extern "C" void SDL1_hax_X11_jpfix(int ro_scan,int yen_scan);
00019 
00020 # define _NET_WM_STATE_REMOVE        0    // remove/unset property
00021 # define _NET_WM_STATE_ADD           1    // add/set property
00022 # define _NET_WM_STATE_TOGGLE        2    // toggle property
00023 #endif
00024 
00025 void LinuxX11_OnTop(bool f) {
00026     (void)f;
00027 
00028 #if !defined(C_SDL2)
00029     SDL_SysWMinfo wminfo;
00030     memset(&wminfo,0,sizeof(wminfo));
00031     SDL_VERSION(&wminfo.version);
00032     if (SDL_GetWMInfo(&wminfo) >= 0) {
00033         if (wminfo.subsystem == SDL_SYSWM_X11 && wminfo.info.x11.display != NULL) {
00034             Atom wmStateAbove = XInternAtom(wminfo.info.x11.display, "_NET_WM_STATE_ABOVE", 1);
00035             if (wmStateAbove == None) return;
00036 
00037             Atom wmNetWmState = XInternAtom(wminfo.info.x11.display, "_NET_WM_STATE", 1);
00038             if (wmNetWmState == None) return;
00039 
00040             XClientMessageEvent xclient;
00041             memset(&xclient,0,sizeof(xclient));
00042 
00043             xclient.type = ClientMessage;
00044             xclient.window = wminfo.info.x11.wmwindow;
00045             xclient.message_type = wmNetWmState;
00046             xclient.format = 32;
00047             xclient.data.l[0] = f ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
00048             xclient.data.l[1] = (long int)wmStateAbove;
00049             xclient.data.l[2] = 0;
00050             xclient.data.l[3] = 0;
00051             xclient.data.l[4] = 0;
00052 
00053             XSendEvent(
00054                     wminfo.info.x11.display,
00055                     DefaultRootWindow(wminfo.info.x11.display),
00056                     False,
00057                     SubstructureRedirectMask | SubstructureNotifyMask,
00058                     (XEvent *)&xclient );
00059         }
00060     }
00061 #endif
00062 }
00063 
00064 char *LinuxX11_KeySymName(Uint32 x) {
00065     (void)x;
00066 #if !defined(C_SDL2)
00067     SDL_SysWMinfo wminfo;
00068     memset(&wminfo,0,sizeof(wminfo));
00069     SDL_VERSION(&wminfo.version);
00070     if (SDL_GetWMInfo(&wminfo) >= 0) {
00071         if (wminfo.subsystem == SDL_SYSWM_X11 && wminfo.info.x11.display != NULL) {
00072             return XKeysymToString(x);
00073         }
00074     }
00075 #endif
00076 
00077     return NULL;
00078 }
00079 
00080 void Linux_JPXKBFix(void) {
00081 #if !defined(C_SDL2)
00082     SDL_SysWMinfo wminfo;
00083     memset(&wminfo,0,sizeof(wminfo));
00084     SDL_VERSION(&wminfo.version);
00085     if (SDL_GetWMInfo(&wminfo) >= 0) {
00086         if (wminfo.subsystem == SDL_SYSWM_X11 && wminfo.info.x11.display != NULL) {
00087             /* detect xkbmap with Ro and Yen keys mapped to \, determine the scan codes,
00088              * and then hack the SDL 1.x X11 driver to handle it.
00089              *
00090              * Never assume specific scan codes, even though on my system the scan codes
00091              * are 97 (Yen) and 132 (Ro) both mapped to \ backslash.
00092              *
00093              * We can use the index to look for keysyms that mention backslash and underscore (Ro)
00094              * or backslash and bar (Yen) */
00095             /* TODO: If xkbmap actually maps these keys properly, how can we tell? */
00096             int ro=-1,yen=-1;
00097             unsigned int i,j;
00098             KeySym sym[4];
00099 
00100             for (i=0;i < 256;i++) {
00101                 for (j=0;j < 4;j++)
00102                     sym[j] = XKeycodeToKeysym(wminfo.info.x11.display,(KeyCode)i,(int)j);
00103 
00104                 if (sym[0] == '\\') {
00105                     if (sym[1] == '_') { /* shift + backslash == _   means we found Ro */
00106                         if (ro < 0) ro = (int)i;
00107                     }
00108                     else if (sym[1] == '|') { /* shift + backslash == |   means we found Yen */
00109                         if (yen < 0) yen = (int)i;
00110                     }
00111                 }
00112             }
00113 
00114             LOG_MSG("JP Linux/X11 fix: Found Ro=%d Yen=%d",ro,yen);
00115 
00116             SDL1_hax_X11_jpfix(ro,yen);
00117         }
00118     }
00119 #endif
00120 }
00121 
00122 unsigned int Linux_GetKeyboardLayout(void) {
00123     unsigned int ret = DKM_US;
00124 
00125 #if defined(C_SDL2)
00126     // TODO
00127 #else
00128     SDL_SysWMinfo wminfo;
00129     memset(&wminfo,0,sizeof(wminfo));
00130     SDL_VERSION(&wminfo.version);
00131     if (SDL_GetWMInfo(&wminfo) >= 0) {
00132         if (wminfo.subsystem == SDL_SYSWM_X11 && wminfo.info.x11.display != NULL) {
00133             XkbRF_VarDefsRec vd = {0};
00134             XkbStateRec state = {0};
00135 
00136             XkbGetState(wminfo.info.x11.display, XkbUseCoreKbd, &state);
00137 
00138             XkbDescPtr desc = XkbGetKeyboard(wminfo.info.x11.display, XkbAllComponentsMask, XkbUseCoreKbd);
00139             char *group = desc ? XGetAtomName(wminfo.info.x11.display, desc->names->groups[state.group]) : NULL;
00140 
00141             if (group != NULL) LOG_MSG("Current X11 keyboard layout (full name) is: '%s'\n",group);
00142 
00143             /* FIXME: Does this allocate anything? Do I need to free it? I am concerned about memory leaks --J.C. */
00144             XkbRF_GetNamesProp(wminfo.info.x11.display, NULL, &vd);
00145 
00146             char *tok = vd.layout ? strtok(vd.layout, ",") : NULL;
00147 
00148             if (tok != NULL) {
00149                 for (int i = 0; i < state.group; i++) {
00150                     tok = strtok(NULL, ",");
00151                     if (tok == NULL) break;
00152                 }
00153                 if (tok != NULL) {
00154                     LOG_MSG("Current X11 keyboard layout (token) is: '%s'\n",tok);
00155 
00156                     if (!strcmp(tok,"us"))
00157                         ret = DKM_US;
00158                     else if (!strcmp(tok,"jp"))
00159                         ret = DKM_JPN;
00160                     else if (!strcmp(tok,"de"))
00161                         ret = DKM_DEU;
00162                 }
00163             }
00164 
00165             if (group) XFree(group);
00166             if (desc) XFree(desc);
00167         }
00168     }
00169 #endif
00170 
00171     return ret;
00172 }
00173 
00174 void UpdateWindowDimensions_Linux(void) {
00175 #if defined(C_SDL2)
00176     // TODO
00177 #else
00178     bool GFX_IsFullscreen();
00179 
00180     SDL_SysWMinfo wminfo;
00181     memset(&wminfo,0,sizeof(wminfo));
00182     SDL_VERSION(&wminfo.version);
00183     if (SDL_GetWMInfo(&wminfo) >= 0) {
00184         if (wminfo.subsystem == SDL_SYSWM_X11 && wminfo.info.x11.display != NULL) {
00185             XWindowAttributes attr;
00186             bool maximized = false;
00187 
00188             memset(&attr,0,sizeof(attr));
00189             XGetWindowAttributes(wminfo.info.x11.display, GFX_IsFullscreen() ? wminfo.info.x11.fswindow : wminfo.info.x11.wmwindow, &attr);
00190 
00191             /* we also want to ask X11 if the various atoms have been set by the window manager
00192              * to signal that our window has been maximized. generally when the window has been
00193              * maximized, SDL video mode setting has no effect on the dimensions of the window
00194              * when it's been maximized so it helps to know in order to make better choices */
00195             if (GFX_IsFullscreen()) {
00196             }
00197             else {
00198                 Atom atomWmState = XInternAtom(wminfo.info.x11.display, "_NET_WM_STATE",                False);
00199                 Atom atomMaxVert = XInternAtom(wminfo.info.x11.display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
00200                 Atom atomMaxHorz = XInternAtom(wminfo.info.x11.display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
00201 
00202                 unsigned long nItem = 0, bytesAfter = 0;
00203                 unsigned char *properties = NULL;
00204                 Atom type = None;
00205                 int format = 0;
00206 
00207                 XGetWindowProperty(wminfo.info.x11.display, wminfo.info.x11.wmwindow,
00208                     /*atom to query*/atomWmState,/*offset*/0,/*length*/16384,/*delete*/False,
00209                     /*request type*/AnyPropertyType,
00210                     /*returns...*/&type,&format,&nItem,&bytesAfter,&properties);
00211 
00212                 if (properties != NULL) {
00213                     if (format == 32) { /* it usually is */
00214                         for (unsigned long i=0;i < nItem;i++) {
00215                             uint32_t val = ((uint32_t*)properties)[i];
00216 
00217                             if ((Atom)val == atomMaxVert || (Atom)val == atomMaxHorz)
00218                                 maximized = true;
00219                         }
00220                     }
00221 
00222                     XFree(properties);
00223                 }
00224             }
00225 
00226             LOG_MSG("X11 main window is %u x %u maximized=%u",attr.width, attr.height, maximized);
00227 
00228             UpdateWindowDimensions((unsigned int)attr.width, (unsigned int)attr.height);
00229             UpdateWindowMaximized(maximized);
00230         }
00231     }
00232 #endif
00233 }
00234 
00235 void Linux_GetDesktopResolution(int *width,int *height) {
00236 #if defined(C_SDL2)
00237     *width = 1024; // guess
00238     *height = 768;
00239 #else
00240         /* We're most likely running on an X-windows desktop (through SDL). */
00241         SDL_SysWMinfo wminfo;
00242         memset(&wminfo,0,sizeof(wminfo));
00243         SDL_VERSION(&wminfo.version);
00244         if (SDL_GetWMInfo(&wminfo) >= 0) {
00245                 if (wminfo.subsystem == SDL_SYSWM_X11 && wminfo.info.x11.display != NULL) {
00246                         LOG_MSG("GetDesktopResolution reading X11 desktop resolution");
00247 
00248                         Window rootWindow = DefaultRootWindow(wminfo.info.x11.display);
00249                         if (rootWindow != 0) {
00250                                 XWindowAttributes rootWinAttr;
00251 
00252                                 memset(&rootWinAttr,0,sizeof(rootWinAttr));
00253                                 XGetWindowAttributes(wminfo.info.x11.display,rootWindow,&rootWinAttr);
00254                                 LOG_MSG("Root window (ID %lu) is %lu x %lu",(unsigned long)rootWindow,(unsigned long)rootWinAttr.width,(unsigned long)rootWinAttr.height);
00255 
00256                                 if (rootWinAttr.width > 0 && rootWinAttr.height > 0) {
00257                                         *width = (int)rootWinAttr.width;
00258                                         *height = (int)rootWinAttr.height;
00259                                 }
00260                                 else {
00261                                         *width = 1024; // guess
00262                                         *height = 768;
00263                                 }
00264                         }
00265                         else {
00266                                 *width = 1024; // guess
00267                                 *height = 768;
00268                         }
00269                 }
00270                 else {
00271                         *width = 1024; // guess
00272                         *height = 768;
00273                 }
00274         }
00275         else {
00276                 *width = 1024; // guess
00277                 *height = 768;
00278         }
00279 #endif
00280 }
00281 #endif
00282