#if defined(SUPPORT_X11) #include #include #include #include #if !defined(GLES) #include #include #endif #include "types.h" #include "cfg/cfg.h" #include "linux-dist/x11.h" #include "linux-dist/main.h" #if FEAT_HAS_NIXPROF #include "profiler/profiler.h" #endif #if defined(TARGET_PANDORA) #define DEFAULT_FULLSCREEN 1 #define DEFAULT_WINDOW_WIDTH 800 #else #define DEFAULT_FULLSCREEN 0 #define DEFAULT_WINDOW_WIDTH 640 #endif #define DEFAULT_WINDOW_HEIGHT 480 map x11_keymap; int x11_dc_buttons = 0xFFFF; int x11_keyboard_input = 0; int x11_width; int x11_height; int ndcid = 0; void* x11_glc = NULL; bool x11_fullscreen = false; Atom wmDeleteMessage; void* x11_vis; extern bool dump_frame_switch; void dc_stop(void); bool dc_loadstate(void); bool dc_savestate(void); enum { _NET_WM_STATE_REMOVE =0, _NET_WM_STATE_ADD = 1, _NET_WM_STATE_TOGGLE =2 }; void x11_window_set_fullscreen(bool fullscreen) { XEvent xev; xev.xclient.type = ClientMessage; xev.xclient.window = (Window)x11_win; xev.xclient.message_type = XInternAtom((Display*)x11_disp, "_NET_WM_STATE", False); xev.xclient.format = 32; xev.xclient.data.l[0] = 2; // _NET_WM_STATE_TOGGLE xev.xclient.data.l[1] = XInternAtom((Display*)x11_disp, "_NET_WM_STATE_FULLSCREEN", True); xev.xclient.data.l[2] = 0; // no second property to toggle xev.xclient.data.l[3] = 1; xev.xclient.data.l[4] = 0; printf("x11: setting fullscreen to %d\n", fullscreen); XSendEvent((Display*)x11_disp, DefaultRootWindow((Display*)x11_disp), False, SubstructureNotifyMask, &xev); } void event_x11_handle() { XEvent event; while(XPending((Display *)x11_disp)) { XNextEvent((Display *)x11_disp, &event); if (event.type == ClientMessage && event.xclient.data.l[0] == wmDeleteMessage) dc_stop(); } } u8 kb_map[256]; static void init_kb_map() { //04-1D Letter keys A-Z (in alphabetic order) kb_map[KEY_A] = 0x04; kb_map[KEY_B] = 0x05; kb_map[KEY_C] = 0x06; kb_map[KEY_D] = 0x07; kb_map[KEY_E] = 0x08; kb_map[KEY_F] = 0x09; kb_map[KEY_G] = 0x0A; kb_map[KEY_H] = 0x0B; kb_map[KEY_I] = 0x0C; kb_map[KEY_J] = 0x0D; kb_map[KEY_K] = 0x0E; kb_map[KEY_L] = 0x0F; kb_map[KEY_M] = 0x10; kb_map[KEY_N] = 0x11; kb_map[KEY_O] = 0x12; kb_map[KEY_P] = 0x13; kb_map[KEY_Q] = 0x14; kb_map[KEY_R] = 0x15; kb_map[KEY_S] = 0x16; kb_map[KEY_T] = 0x17; kb_map[KEY_U] = 0x18; kb_map[KEY_V] = 0x19; kb_map[KEY_W] = 0x1A; kb_map[KEY_X] = 0x1B; kb_map[KEY_Y] = 0x1C; kb_map[KEY_Z] = 0x1D; //1E-27 Number keys 1-0 kb_map[KEY_1] = 0x1E; kb_map[KEY_2] = 0x1F; kb_map[KEY_3] = 0x20; kb_map[KEY_4] = 0x21; kb_map[KEY_5] = 0x22; kb_map[KEY_6] = 0x23; kb_map[KEY_7] = 0x24; kb_map[KEY_8] = 0x25; kb_map[KEY_9] = 0x26; kb_map[KEY_0] = 0x27; kb_map[KEY_RETURN] = 0x28; kb_map[KEY_ESC] = 0x29; kb_map[KEY_BACKSPACE] = 0x2A; kb_map[KEY_TAB] = 0x2B; kb_map[KEY_SPACE] = 0x2C; kb_map[20] = 0x2D; // - kb_map[21] = 0x2E; // = kb_map[34] = 0x2F; // [ kb_map[35] = 0x30; // ] kb_map[94] = 0x31; // \ (US) unsure of keycode //32-34 "]", ";" and ":" (the 3 keys right of L) kb_map[51] = 0x32; // ~ (non-US) *,µ in FR layout kb_map[47] = 0x33; // ; kb_map[48] = 0x34; // ' //35 hankaku/zenkaku / kanji (top left) kb_map[49] = 0x35; // `~ (US) //36-38 ",", "." and "/" (the 3 keys right of M) kb_map[59] = 0x36; kb_map[60] = 0x37; kb_map[61] = 0x38; // CAPSLOCK kb_map[66] = 0x39; //3A-45 Function keys F1-F12 for (int i = 0;i < 10; i++) kb_map[KEY_F1 + i] = 0x3A + i; kb_map[KEY_F11] = 0x44; kb_map[KEY_F12] = 0x45; //46-4E Control keys above cursor keys kb_map[107] = 0x46; // Print Screen kb_map[78] = 0x47; // Scroll Lock kb_map[127] = 0x48; // Pause kb_map[KEY_INS] = 0x49; kb_map[KEY_HOME] = 0x4A; kb_map[KEY_PGUP] = 0x4B; kb_map[KEY_DEL] = 0x4C; kb_map[KEY_END] = 0x4D; kb_map[KEY_PGDOWN] = 0x4E; //4F-52 Cursor keys kb_map[KEY_RIGHT] = 0x4F; kb_map[KEY_LEFT] = 0x50; kb_map[KEY_DOWN] = 0x51; kb_map[KEY_UP] = 0x52; //53 Num Lock (Numeric keypad) kb_map[77] = 0x53; //54 "/" (Numeric keypad) kb_map[106] = 0x54; //55 "*" (Numeric keypad) kb_map[63] = 0x55; //56 "-" (Numeric keypad) kb_map[82] = 0x56; //57 "+" (Numeric keypad) kb_map[86] = 0x57; //58 Enter (Numeric keypad) kb_map[104] = 0x58; //59-62 Number keys 1-0 (Numeric keypad) kb_map[87] = 0x59; kb_map[88] = 0x5A; kb_map[89] = 0x5B; kb_map[83] = 0x5C; kb_map[84] = 0x5D; kb_map[85] = 0x5E; kb_map[79] = 0x5F; kb_map[80] = 0x60; kb_map[81] = 0x61; kb_map[90] = 0x62; //63 "." (Numeric keypad) kb_map[91] = 0x63; //64 #| (non-US) //kb_map[94] = 0x64; //65 S3 key //66-A4 Not used //A5-DF Reserved //E0 Left Control //E1 Left Shift //E2 Left Alt //E3 Left S1 //E4 Right Control //E5 Right Shift //E6 Right Alt //E7 Right S3 //E8-FF Reserved } static u32 kb_used = 0; extern u8 kb_shift; // shift keys pressed (bitmask) extern u8 kb_led; // leds currently lit extern u8 kb_key[6]; // normal keys pressed extern u32 mo_buttons; extern f32 mo_x_delta; extern f32 mo_y_delta; extern f32 mo_wheel_delta; static bool capturing_mouse; static Cursor empty_cursor = None; static Cursor create_empty_cursor() { if (empty_cursor == None) { Display *display = (Display*)x11_disp; char data[] = { 0 }; XColor color; color.red = color.green = color.blue = 0; Pixmap pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display), data, 1, 1); if (pixmap) { empty_cursor = XCreatePixmapCursor(display, pixmap, pixmap, &color, &color, 0, 0); XFreePixmap(display, pixmap); } } return empty_cursor; } static void destroy_empty_cursor() { if (empty_cursor != None) { XFreeCursor((Display*)x11_disp, empty_cursor); empty_cursor = None; } } static void x11_capture_mouse() { x11_window_set_text("Reicast - mouse capture"); capturing_mouse = true; Cursor cursor = create_empty_cursor(); Display *display = (Display*)x11_disp; Window window = (Window)x11_win; XDefineCursor(display, window, cursor); XGrabPointer(display, window, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); } static void x11_uncapture_mouse() { x11_window_set_text("Reicast"); capturing_mouse = false; Display *display = (Display*)x11_disp; Window window = (Window)x11_win; XUndefineCursor(display, window); XUngrabPointer(display, CurrentTime); } void input_x11_handle() { if (!x11_win || (!x11_keyboard_input && !settings.input.DCKeyboard && !settings.input.DCMouse)) return; //Handle X11 static int prev_x = -1; static int prev_y = -1; bool mouse_moved = false; XEvent e; Display *display = (Display*)x11_disp; while (XCheckWindowEvent(display, (Window)x11_win, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask, &e)) { switch(e.type) { case KeyPress: case KeyRelease: { if (e.type == KeyRelease && XEventsQueued(display, QueuedAfterReading)) { XEvent nev; XPeekEvent(display, &nev); if (nev.type == KeyPress && nev.xkey.time == e.xkey.time && nev.xkey.keycode == e.xkey.keycode) // Key wasn’t actually released: auto repeat continue; } // Dreamcast keyboard emulation if (e.xkey.keycode == KEY_LSHIFT || e.xkey.keycode == KEY_RSHIFT) if (e.type == KeyRelease) kb_shift &= ~(0x02 | 0x20); else kb_shift |= (0x02 | 0x20); if (e.xkey.keycode == KEY_LCTRL || e.xkey.keycode == KEY_RCTRL) if (e.type == KeyRelease) kb_shift &= ~(0x01 | 0x10); else kb_shift |= (0x01 | 0x10); u8 dc_keycode = kb_map[e.xkey.keycode & 0xFF]; if (dc_keycode != 0) { if (e.type == KeyPress) { if (kb_used < 6) { bool found = false; for (int i = 0; !found && i < 6; i++) { if (kb_key[i] == dc_keycode) found = true; } if (!found) { kb_key[kb_used] = dc_keycode; kb_used++; } } } else { if (kb_used > 0) { for (int i = 0; i < 6; i++) { if (kb_key[i] == dc_keycode) { kb_used--; for (int j = i; j < 5; j++) kb_key[j] = kb_key[j + 1]; kb_key[5] = 0; } } } } } if (settings.input.DCMouse) { // Start/stop mouse capture with Left Ctrl + Left Alt if (e.type == KeyPress && ((e.xkey.keycode == KEY_LALT && (e.xkey.state & ControlMask)) || (e.xkey.keycode == KEY_LCTRL && (e.xkey.state & Mod1Mask)))) { capturing_mouse = !capturing_mouse; if (capturing_mouse) x11_capture_mouse(); else x11_uncapture_mouse(); } } if (x11_keyboard_input) { // Normal keyboard handling if (e.type == KeyRelease && e.xkey.keycode == KEY_ESC && !(e.xkey.state & (ControlMask | ShiftMask | Mod1Mask))) { dc_stop(); } #ifndef RELEASE else if (e.xkey.keycode == KEY_F10) { // Dump the next frame into a file dump_frame_switch = e.type == KeyPress; } #elif FEAT_HAS_NIXPROF else if (e.type == KeyRelease && e.xkey.keycode == KEY_F10) { if (sample_Switch(3000)) { printf("Starting profiling\n"); } else { printf("Stopping profiling\n"); } } #endif else if (e.type == KeyRelease && e.xkey.keycode == KEY_F11) { x11_fullscreen = !x11_fullscreen; x11_window_set_fullscreen(x11_fullscreen); } else if (e.type == KeyRelease && e.xkey.keycode == KEY_F2) { dc_savestate() ; } else if (e.type == KeyRelease && e.xkey.keycode == KEY_F4) { dc_loadstate() ; } else { int dc_key = x11_keymap[e.xkey.keycode]; if (dc_key == DC_AXIS_LT) { if (e.type == KeyPress) lt[0] = 255; else lt[0] = 0; } else if (dc_key == DC_AXIS_RT) { if (e.type == KeyPress) rt[0] = 255; else rt[0] = 0; } if (e.type == KeyPress) { kcode[0] &= ~dc_key; } else { kcode[0] |= dc_key; } #if defined(_DEBUG) printf("KEY: %d -> %d: %d\n", e.xkey.keycode, dc_key, x11_dc_buttons ); #endif } } } break; case FocusOut: { if (capturing_mouse) x11_uncapture_mouse(); capturing_mouse = false; } break; case ButtonPress: case ButtonRelease: { u32 button_mask = 0; if (e.xbutton.button == Button1) button_mask = 1 << 2; else if (e.xbutton.button == Button2) button_mask = 1 << 1; if (button_mask) { if (e.type == ButtonPress) mo_buttons &= ~button_mask; else mo_buttons |= button_mask; } } // FALL THROUGH case MotionNotify: if (settings.input.DCMouse) { mouse_moved = true; if (prev_x != -1) mo_x_delta += (f32)(e.xmotion.x - prev_x) * settings.input.MouseSensitivity / 100.f; if (prev_y != -1) mo_y_delta += (f32)(e.xmotion.y - prev_y) * settings.input.MouseSensitivity / 100.f; prev_x = e.xmotion.x; prev_y = e.xmotion.y; } break; } } if (capturing_mouse && mouse_moved) { prev_x = x11_width / 2; prev_y = x11_height / 2; XWarpPointer(display, None, (Window)x11_win, 0, 0, 0, 0, prev_x, prev_y); XSync(display, true); } } void input_x11_init() { x11_keymap[KEY_LEFT] = DC_DPAD_LEFT; x11_keymap[KEY_RIGHT] = DC_DPAD_RIGHT; x11_keymap[KEY_UP] = DC_DPAD_UP; x11_keymap[KEY_DOWN] = DC_DPAD_DOWN; // Layout on a real DC controller // Y // X B // A x11_keymap[KEY_S] = DC_BTN_X; x11_keymap[KEY_X] = DC_BTN_A; x11_keymap[KEY_D] = DC_BTN_Y; x11_keymap[KEY_C] = DC_BTN_B; // Used by some "arcade" controllers x11_keymap[KEY_Q] = DC_BTN_Z; x11_keymap[KEY_W] = DC_BTN_C; x11_keymap[KEY_E] = DC_BTN_D; // Start button (triangle) x11_keymap[KEY_RETURN] = DC_BTN_START; // Shoulder trigger x11_keymap[KEY_F] = DC_AXIS_LT; x11_keymap[KEY_V] = DC_AXIS_RT; x11_keyboard_input = (cfgLoadInt("input", "enable_x11_keyboard", 1) >= 1); if (!x11_keyboard_input) printf("X11 Keyboard input disabled by config.\n"); init_kb_map(); } void x11_window_create() { if (cfgLoadInt("pvr", "nox11", 0) == 0) { XInitThreads(); // X11 variables Window x11Window = 0; Display* x11Display = 0; long x11Screen = 0; XVisualInfo* x11Visual = 0; Colormap x11Colormap = 0; /* Step 0 - Create a NativeWindowType that we can use it for OpenGL ES output */ Window sRootWindow; XSetWindowAttributes sWA; unsigned int ui32Mask; int i32Depth; // Initializes the display and screen x11Display = XOpenDisplay(NULL); if (!x11Display && !(x11Display = XOpenDisplay(":0"))) { printf("Error: Unable to open X display\n"); return; } x11Screen = XDefaultScreen(x11Display); // Gets the window parameters sRootWindow = RootWindow(x11Display, x11Screen); int depth = CopyFromParent; #if !defined(GLES) // Get a matching FB config static int visual_attribs[] = { GLX_X_RENDERABLE , True, GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, GLX_RENDER_TYPE , GLX_RGBA_BIT, GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, GLX_RED_SIZE , 8, GLX_GREEN_SIZE , 8, GLX_BLUE_SIZE , 8, GLX_ALPHA_SIZE , 8, GLX_DEPTH_SIZE , 24, GLX_STENCIL_SIZE , 8, GLX_DOUBLEBUFFER , True, //GLX_SAMPLE_BUFFERS , 1, //GLX_SAMPLES , 4, None }; int glx_major, glx_minor; // FBConfigs were added in GLX version 1.3. if (!glXQueryVersion(x11Display, &glx_major, &glx_minor) || ((glx_major == 1) && (glx_minor < 3)) || (glx_major < 1)) { printf("Invalid GLX version"); exit(1); } int fbcount; GLXFBConfig* fbc = glXChooseFBConfig(x11Display, x11Screen, visual_attribs, &fbcount); if (!fbc) { printf("Failed to retrieve a framebuffer config\n"); exit(1); } printf("Found %d matching FB configs.\n", fbcount); GLXFBConfig bestFbc = fbc[0]; XFree(fbc); // Get a visual XVisualInfo *vi = glXGetVisualFromFBConfig(x11Display, bestFbc); printf("Chosen visual ID = 0x%lx\n", vi->visualid); depth = vi->depth; x11Visual = vi; x11Colormap = XCreateColormap(x11Display, RootWindow(x11Display, x11Screen), vi->visual, AllocNone); #else i32Depth = DefaultDepth(x11Display, x11Screen); x11Visual = new XVisualInfo; XMatchVisualInfo(x11Display, x11Screen, i32Depth, TrueColor, x11Visual); if (!x11Visual) { printf("Error: Unable to acquire visual\n"); return; } x11Colormap = XCreateColormap(x11Display, sRootWindow, x11Visual->visual, AllocNone); #endif sWA.colormap = x11Colormap; // Add to these for handling other events sWA.event_mask = StructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask; if (settings.input.DCMouse) sWA.event_mask |= PointerMotionMask | FocusChangeMask; ui32Mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap; x11_width = cfgLoadInt("x11", "width", DEFAULT_WINDOW_WIDTH); x11_height = cfgLoadInt("x11", "height", DEFAULT_WINDOW_HEIGHT); x11_fullscreen = (cfgLoadInt("x11", "fullscreen", DEFAULT_FULLSCREEN) > 0); if (x11_width < 0 || x11_height < 0) { x11_width = XDisplayWidth(x11Display, x11Screen); x11_height = XDisplayHeight(x11Display, x11Screen); } // Creates the X11 window x11Window = XCreateWindow(x11Display, RootWindow(x11Display, x11Screen), (ndcid%3)*640, (ndcid/3)*480, x11_width, x11_height, 0, depth, InputOutput, x11Visual->visual, ui32Mask, &sWA); // Capture the close window event wmDeleteMessage = XInternAtom(x11Display, "WM_DELETE_WINDOW", False); XSetWMProtocols(x11Display, x11Window, &wmDeleteMessage, 1); if(x11_fullscreen) { // fullscreen Atom wmState = XInternAtom(x11Display, "_NET_WM_STATE", False); Atom wmFullscreen = XInternAtom(x11Display, "_NET_WM_STATE_FULLSCREEN", False); XChangeProperty(x11Display, x11Window, wmState, XA_ATOM, 32, PropModeReplace, (unsigned char *)&wmFullscreen, 1); XMapRaised(x11Display, x11Window); } else { XMapWindow(x11Display, x11Window); } #if !defined(GLES) #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB"); verify(glXCreateContextAttribsARB != 0); int context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 1, GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, None }; x11_glc = glXCreateContextAttribsARB(x11Display, bestFbc, 0, True, context_attribs); XSync(x11Display, False); if (!x11_glc) { die("Failed to create GL3.1 context\n"); } #endif XFlush(x11Display); //(EGLNativeDisplayType)x11Display; x11_disp = (void*)x11Display; x11_win = (void*)x11Window; x11_vis = (void*)x11Visual->visual; x11_window_set_text("Reicast"); } else { printf("Not creating X11 window ..\n"); } } void x11_window_set_text(const char* text) { if (x11_win) { XChangeProperty((Display*)x11_disp, (Window)x11_win, XInternAtom((Display*)x11_disp, "WM_NAME", False), //WM_NAME, XInternAtom((Display*)x11_disp, "UTF8_STRING", False), //UTF8_STRING, 8, PropModeReplace, (const unsigned char *)text, strlen(text)); } } void x11_gl_context_destroy() { glXMakeCurrent((Display*)x11_disp, None, NULL); glXDestroyContext((Display*)x11_disp, x11_glc); } void x11_window_destroy() { // close XWindow if (x11_win) { XDestroyWindow((Display*)x11_disp, (Window)x11_win); x11_win = NULL; } if (x11_disp) { #if !defined(GLES) if (x11_glc) { glXMakeCurrent((Display*)x11_disp, None, NULL); glXDestroyContext((Display*)x11_disp, (GLXContext)x11_glc); x11_glc = NULL; } #endif XCloseDisplay((Display*)x11_disp); x11_disp = NULL; } } #endif