| /* | |
| * X11DRV initialization code | |
| * | |
| * Copyright 1998 Patrik Stridvall | |
| * Copyright 2000 Alexandre Julliard | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include "config.h" | |
| #include <fcntl.h> | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #ifdef HAVE_SYS_TIME_H | |
| # include <sys/time.h> | |
| #endif | |
| #ifdef HAVE_UNISTD_H | |
| # include <unistd.h> | |
| #endif | |
| #include <X11/cursorfont.h> | |
| #include <X11/Xlib.h> | |
| #ifdef HAVE_XKB | |
| #include <X11/XKBlib.h> | |
| #endif | |
| #define BOOL X_BOOL | |
| #define BYTE X_BYTE | |
| #define INT8 X_INT8 | |
| #define INT16 X_INT16 | |
| #define INT32 X_INT32 | |
| #define INT64 X_INT64 | |
| #define LONG64 X_LONG64 | |
| #include <X11/Xproto.h> | |
| #undef BOOL | |
| #undef BYTE | |
| #undef INT8 | |
| #undef INT16 | |
| #undef INT32 | |
| #undef INT64 | |
| #undef LONG64 | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "wine/winbase16.h" | |
| #include "winreg.h" | |
| #include "x11drv.h" | |
| #include "xvidmode.h" | |
| #include "xrandr.h" | |
| #include "dga2.h" | |
| #include "wine/server.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(x11drv); | |
| static CRITICAL_SECTION X11DRV_CritSection; | |
| static CRITICAL_SECTION_DEBUG critsect_debug = | |
| { | |
| 0, 0, &X11DRV_CritSection, | |
| { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, | |
| 0, 0, { 0, (DWORD)(__FILE__ ": X11DRV_CritSection") } | |
| }; | |
| static CRITICAL_SECTION X11DRV_CritSection = { &critsect_debug, -1, 0, 0, 0, 0 }; | |
| Screen *screen; | |
| Visual *visual; | |
| unsigned int screen_width; | |
| unsigned int screen_height; | |
| unsigned int screen_depth; | |
| Window root_window; | |
| DWORD desktop_tid = 0; | |
| int dxgrab = 0; | |
| int usedga = 0; | |
| int usexvidmode = 0; | |
| int usexrandr = 1; | |
| int use_xkb = 1; | |
| int use_take_focus = 1; | |
| int managed_mode = 1; | |
| int client_side_with_core = 1; | |
| int client_side_with_render = 1; | |
| int client_side_antialias_with_core = 1; | |
| int client_side_antialias_with_render = 1; | |
| int using_wine_desktop = 0; | |
| static BOOL synchronous; /* run in synchronous mode? */ | |
| static BOOL desktop_dbl_buf = TRUE; | |
| static char *desktop_geometry; | |
| static XVisualInfo *desktop_vi; | |
| static x11drv_error_callback err_callback; /* current callback for error */ | |
| static Display *err_callback_display; /* display callback is set for */ | |
| static void *err_callback_arg; /* error callback argument */ | |
| static int err_callback_result; /* error callback result */ | |
| static unsigned long err_serial; /* serial number of first request */ | |
| static int (*old_error_handler)( Display *, XErrorEvent * ); | |
| static int use_xim = 1; | |
| static char input_style[20]; | |
| #define IS_OPTION_TRUE(ch) \ | |
| ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1') | |
| #define IS_OPTION_FALSE(ch) \ | |
| ((ch) == 'n' || (ch) == 'N' || (ch) == 'f' || (ch) == 'F' || (ch) == '0') | |
| /*********************************************************************** | |
| * ignore_error | |
| * | |
| * Check if the X error is one we can ignore. | |
| */ | |
| static inline BOOL ignore_error( Display *display, XErrorEvent *event ) | |
| { | |
| if (event->request_code == X_SetInputFocus && event->error_code == BadMatch) return TRUE; | |
| return FALSE; | |
| } | |
| /*********************************************************************** | |
| * X11DRV_expect_error | |
| * | |
| * Setup a callback function that will be called on an X error. The | |
| * callback must return non-zero if the error is the one it expected. | |
| * This function acquires the x11 lock; X11DRV_check_error must be | |
| * called in all cases to release it. | |
| */ | |
| void X11DRV_expect_error( Display *display, x11drv_error_callback callback, void *arg ) | |
| { | |
| wine_tsx11_lock(); | |
| err_callback = callback; | |
| err_callback_display = display; | |
| err_callback_arg = arg; | |
| err_callback_result = 0; | |
| err_serial = NextRequest(display); | |
| } | |
| /*********************************************************************** | |
| * X11DRV_check_error | |
| * | |
| * Check if an expected X11 error occurred; return non-zero if yes. | |
| * Also release the x11 lock obtained in X11DRV_expect_error. | |
| * The caller is responsible for calling XSync first if necessary. | |
| */ | |
| int X11DRV_check_error(void) | |
| { | |
| int ret; | |
| err_callback = NULL; | |
| ret = err_callback_result; | |
| wine_tsx11_unlock(); | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * error_handler | |
| */ | |
| static int error_handler( Display *display, XErrorEvent *error_evt ) | |
| { | |
| if (err_callback && display == err_callback_display && | |
| (long)(error_evt->serial - err_serial) >= 0) | |
| { | |
| if ((err_callback_result = err_callback( display, error_evt, err_callback_arg ))) | |
| { | |
| TRACE( "got expected error %d req %d\n", | |
| error_evt->error_code, error_evt->request_code ); | |
| return 0; | |
| } | |
| } | |
| if (ignore_error( display, error_evt )) | |
| { | |
| TRACE( "got ignored error %d req %d\n", | |
| error_evt->error_code, error_evt->request_code ); | |
| return 0; | |
| } | |
| if (synchronous) | |
| { | |
| ERR( "X protocol error: serial=%ld, request_code=%d - breaking into debugger\n", | |
| error_evt->serial, error_evt->request_code ); | |
| DebugBreak(); /* force an entry in the debugger */ | |
| } | |
| old_error_handler( display, error_evt ); | |
| return 0; | |
| } | |
| /*********************************************************************** | |
| * wine_tsx11_lock (X11DRV.@) | |
| */ | |
| void wine_tsx11_lock(void) | |
| { | |
| EnterCriticalSection( &X11DRV_CritSection ); | |
| } | |
| /*********************************************************************** | |
| * wine_tsx11_unlock (X11DRV.@) | |
| */ | |
| void wine_tsx11_unlock(void) | |
| { | |
| LeaveCriticalSection( &X11DRV_CritSection ); | |
| } | |
| /*********************************************************************** | |
| * get_config_key | |
| * | |
| * Get a config key from either the app-specific or the default config | |
| */ | |
| inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name, | |
| char *buffer, DWORD size ) | |
| { | |
| if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, buffer, &size )) return 0; | |
| return RegQueryValueExA( defkey, name, 0, NULL, buffer, &size ); | |
| } | |
| /*********************************************************************** | |
| * setup_options | |
| * | |
| * Setup the x11drv options. | |
| */ | |
| static void setup_options(void) | |
| { | |
| char buffer[MAX_PATH+16]; | |
| HKEY hkey, appkey = 0; | |
| DWORD len; | |
| if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\x11drv", 0, NULL, | |
| REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL )) | |
| { | |
| ERR("Cannot create config registry key\n" ); | |
| ExitProcess(1); | |
| } | |
| /* open the app-specific key */ | |
| len = (GetModuleFileNameA( 0, buffer, MAX_PATH )); | |
| if (len && len < MAX_PATH) | |
| { | |
| HKEY tmpkey; | |
| char *p, *appname = buffer; | |
| if ((p = strrchr( appname, '/' ))) appname = p + 1; | |
| if ((p = strrchr( appname, '\\' ))) appname = p + 1; | |
| strcat( appname, "\\x11drv" ); | |
| if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey )) | |
| { | |
| if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0; | |
| RegCloseKey( tmpkey ); | |
| } | |
| } | |
| if (!get_config_key( hkey, appkey, "Desktop", buffer, sizeof(buffer) )) | |
| { | |
| /* Imperfect validation: If Desktop=N, then we don't turn on | |
| ** the --desktop option. We should really validate for a correct | |
| ** sizing entry */ | |
| if (!IS_OPTION_FALSE(buffer[0])) desktop_geometry = strdup(buffer); | |
| } | |
| if (!get_config_key( hkey, appkey, "Managed", buffer, sizeof(buffer) )) | |
| managed_mode = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "DXGrab", buffer, sizeof(buffer) )) | |
| dxgrab = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "UseDGA", buffer, sizeof(buffer) )) | |
| usedga = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "UseXVidMode", buffer, sizeof(buffer) )) | |
| usexvidmode = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "UseXRandR", buffer, sizeof(buffer) )) | |
| usexrandr = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "UseTakeFocus", buffer, sizeof(buffer) )) | |
| use_take_focus = IS_OPTION_TRUE( buffer[0] ); | |
| screen_depth = 0; | |
| if (!get_config_key( hkey, appkey, "ScreenDepth", buffer, sizeof(buffer) )) | |
| screen_depth = atoi(buffer); | |
| if (!get_config_key( hkey, appkey, "Synchronous", buffer, sizeof(buffer) )) | |
| synchronous = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "ClientSideWithCore", buffer, sizeof(buffer) )) | |
| client_side_with_core = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "ClientSideWithRender", buffer, sizeof(buffer) )) | |
| client_side_with_render = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "ClientSideAntiAliasWithCore", buffer, sizeof(buffer) )) | |
| client_side_antialias_with_core = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "ClientSideAntiAliasWithRender", buffer, sizeof(buffer) )) | |
| client_side_antialias_with_render = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "DesktopDoubleBuffered", buffer, sizeof(buffer) )) | |
| desktop_dbl_buf = IS_OPTION_TRUE( buffer[0] ); | |
| if (!get_config_key( hkey, appkey, "UseXIM", buffer, sizeof(buffer) )) | |
| use_xim = IS_OPTION_TRUE( buffer[0] ); | |
| get_config_key( hkey, appkey, "InputStyle", input_style, sizeof(input_style) ); | |
| if (appkey) RegCloseKey( appkey ); | |
| RegCloseKey( hkey ); | |
| } | |
| /*********************************************************************** | |
| * X11DRV process initialisation routine | |
| */ | |
| static BOOL process_attach(void) | |
| { | |
| Display *display; | |
| setup_options(); | |
| /* Open display */ | |
| if (!(display = XOpenDisplay( NULL ))) return FALSE; | |
| fcntl( ConnectionNumber(display), F_SETFD, 1 ); /* set close on exec flag */ | |
| screen = DefaultScreenOfDisplay( display ); | |
| visual = DefaultVisual( display, DefaultScreen(display) ); | |
| root_window = DefaultRootWindow( display ); | |
| old_error_handler = XSetErrorHandler( error_handler ); | |
| /* Initialize screen depth */ | |
| if (screen_depth) /* depth specified */ | |
| { | |
| int depth_count, i; | |
| int *depth_list = XListDepths(display, DefaultScreen(display), &depth_count); | |
| for (i = 0; i < depth_count; i++) | |
| if (depth_list[i] == screen_depth) break; | |
| XFree( depth_list ); | |
| if (i >= depth_count) | |
| { | |
| WARN( "invalid depth %d, using default\n", screen_depth ); | |
| screen_depth = 0; | |
| } | |
| } | |
| if (!screen_depth) screen_depth = DefaultDepthOfScreen( screen ); | |
| /* Initialize OpenGL */ | |
| X11DRV_OpenGL_Init(display); | |
| /* If OpenGL is available, change the default visual, etc as necessary */ | |
| if (desktop_dbl_buf && (desktop_vi = X11DRV_setup_opengl_visual( display ))) | |
| { | |
| visual = desktop_vi->visual; | |
| screen = ScreenOfDisplay(display, desktop_vi->screen); | |
| screen_depth = desktop_vi->depth; | |
| } | |
| if (synchronous) XSynchronize( display, True ); | |
| screen_width = WidthOfScreen( screen ); | |
| screen_height = HeightOfScreen( screen ); | |
| X11DRV_Settings_Init(); | |
| if (desktop_geometry) | |
| { | |
| root_window = X11DRV_create_desktop( desktop_vi, desktop_geometry ); | |
| using_wine_desktop = 1; | |
| } | |
| /* initialize GDI */ | |
| X11DRV_GDI_Initialize( display ); | |
| #ifdef HAVE_LIBXXF86VM | |
| /* initialize XVidMode */ | |
| X11DRV_XF86VM_Init(); | |
| #endif | |
| #ifdef HAVE_LIBXRANDR | |
| /* initialize XRandR */ | |
| X11DRV_XRandR_Init(); | |
| #endif | |
| #ifdef HAVE_LIBXXF86DGA2 | |
| /* initialize DGA2 */ | |
| X11DRV_XF86DGA2_Init(); | |
| #endif | |
| X11DRV_InitKeyboard(); | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * X11DRV thread termination routine | |
| */ | |
| static void thread_detach(void) | |
| { | |
| struct x11drv_thread_data *data = NtCurrentTeb()->driver_data; | |
| if (data) | |
| { | |
| CloseHandle( data->display_fd ); | |
| wine_tsx11_lock(); | |
| XCloseDisplay( data->display ); | |
| /* if (data->xim) XCloseIM( data->xim ); */ /* crashes Xlib */ | |
| wine_tsx11_unlock(); | |
| HeapFree( GetProcessHeap(), 0, data ); | |
| } | |
| } | |
| /*********************************************************************** | |
| * X11DRV process termination routine | |
| */ | |
| static void process_detach(void) | |
| { | |
| #ifdef HAVE_LIBXXF86DGA2 | |
| /* cleanup DGA2 */ | |
| X11DRV_XF86DGA2_Cleanup(); | |
| #endif | |
| #ifdef HAVE_LIBXXF86VM | |
| /* cleanup XVidMode */ | |
| X11DRV_XF86VM_Cleanup(); | |
| #endif | |
| if(using_client_side_fonts) | |
| X11DRV_XRender_Finalize(); | |
| /* cleanup GDI */ | |
| X11DRV_GDI_Finalize(); | |
| DeleteCriticalSection( &X11DRV_CritSection ); | |
| } | |
| /*********************************************************************** | |
| * X11DRV thread initialisation routine | |
| */ | |
| struct x11drv_thread_data *x11drv_init_thread_data(void) | |
| { | |
| struct x11drv_thread_data *data; | |
| if (!(data = HeapAlloc( GetProcessHeap(), 0, sizeof(*data) ))) | |
| { | |
| ERR( "could not create data\n" ); | |
| ExitProcess(1); | |
| } | |
| wine_tsx11_lock(); | |
| if (!(data->display = XOpenDisplay(NULL))) | |
| { | |
| wine_tsx11_unlock(); | |
| MESSAGE( "x11drv: Can't open display: %s\n", XDisplayName(NULL) ); | |
| MESSAGE( "Please ensure that your X server is running and that $DISPLAY is set correctly.\n" ); | |
| ExitProcess(1); | |
| } | |
| fcntl( ConnectionNumber(data->display), F_SETFD, 1 ); /* set close on exec flag */ | |
| #ifdef HAVE_XKB | |
| if (use_xkb) | |
| { | |
| use_xkb = XkbUseExtension( data->display, NULL, NULL ); | |
| if (use_xkb) XkbSetDetectableAutoRepeat( data->display, True, NULL ); | |
| } | |
| #endif | |
| if (synchronous) XSynchronize( data->display, True ); | |
| wine_tsx11_unlock(); | |
| if (use_xim && !(data->xim = X11DRV_SetupXIM( data->display, input_style ))) | |
| WARN("Input Method is not available\n"); | |
| if (wine_server_fd_to_handle( ConnectionNumber(data->display), GENERIC_READ | SYNCHRONIZE, | |
| FALSE, &data->display_fd )) | |
| { | |
| MESSAGE( "x11drv: Can't allocate handle for display fd\n" ); | |
| ExitProcess(1); | |
| } | |
| data->process_event_count = 0; | |
| data->cursor = None; | |
| data->cursor_window = None; | |
| data->last_focus = 0; | |
| data->selection_wnd = 0; | |
| NtCurrentTeb()->driver_data = data; | |
| if (desktop_tid) AttachThreadInput( GetCurrentThreadId(), desktop_tid, TRUE ); | |
| return data; | |
| } | |
| /*********************************************************************** | |
| * X11DRV initialisation routine | |
| */ | |
| BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) | |
| { | |
| BOOL ret = TRUE; | |
| switch(reason) | |
| { | |
| case DLL_PROCESS_ATTACH: | |
| ret = process_attach(); | |
| break; | |
| case DLL_THREAD_DETACH: | |
| thread_detach(); | |
| break; | |
| case DLL_PROCESS_DETACH: | |
| process_detach(); | |
| break; | |
| } | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * GetScreenSaveActive (X11DRV.@) | |
| * | |
| * Returns the active status of the screen saver | |
| */ | |
| BOOL X11DRV_GetScreenSaveActive(void) | |
| { | |
| int timeout, temp; | |
| wine_tsx11_lock(); | |
| XGetScreenSaver(gdi_display, &timeout, &temp, &temp, &temp); | |
| wine_tsx11_unlock(); | |
| return timeout != 0; | |
| } | |
| /*********************************************************************** | |
| * SetScreenSaveActive (X11DRV.@) | |
| * | |
| * Activate/Deactivate the screen saver | |
| */ | |
| void X11DRV_SetScreenSaveActive(BOOL bActivate) | |
| { | |
| int timeout, interval, prefer_blanking, allow_exposures; | |
| static int last_timeout = 15 * 60; | |
| wine_tsx11_lock(); | |
| XGetScreenSaver(gdi_display, &timeout, &interval, &prefer_blanking, | |
| &allow_exposures); | |
| if (timeout) last_timeout = timeout; | |
| timeout = bActivate ? last_timeout : 0; | |
| XSetScreenSaver(gdi_display, timeout, interval, prefer_blanking, | |
| allow_exposures); | |
| wine_tsx11_unlock(); | |
| } |