2015-08-15 03:50:08 +00:00
|
|
|
|
#if defined(SUPPORT_X11)
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
|
|
|
|
|
|
#if !defined(GLES)
|
2015-08-17 21:46:28 +00:00
|
|
|
|
#include <GL/gl.h>
|
|
|
|
|
#include <GL/glx.h>
|
2015-08-15 03:50:08 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "types.h"
|
|
|
|
|
#include "cfg/cfg.h"
|
|
|
|
|
#include "linux-dist/x11.h"
|
|
|
|
|
#include "linux-dist/main.h"
|
|
|
|
|
|
2015-08-24 22:54:23 +00:00
|
|
|
|
#if FEAT_HAS_NIXPROF
|
|
|
|
|
#include "profiler/profiler.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2015-08-15 03:50:08 +00:00
|
|
|
|
#if defined(TARGET_PANDORA)
|
2015-08-21 00:16:48 +00:00
|
|
|
|
#define DEFAULT_FULLSCREEN 1
|
|
|
|
|
#define DEFAULT_WINDOW_WIDTH 800
|
2015-08-15 03:50:08 +00:00
|
|
|
|
#else
|
2015-08-21 00:16:48 +00:00
|
|
|
|
#define DEFAULT_FULLSCREEN 0
|
|
|
|
|
#define DEFAULT_WINDOW_WIDTH 640
|
2015-08-15 03:50:08 +00:00
|
|
|
|
#endif
|
2015-08-21 00:16:48 +00:00
|
|
|
|
#define DEFAULT_WINDOW_HEIGHT 480
|
2015-08-15 03:50:08 +00:00
|
|
|
|
|
|
|
|
|
map<int, int> x11_keymap;
|
|
|
|
|
int x11_dc_buttons = 0xFFFF;
|
2015-08-15 23:14:34 +00:00
|
|
|
|
int x11_keyboard_input = 0;
|
2015-08-15 03:50:08 +00:00
|
|
|
|
|
2015-12-13 12:47:56 +00:00
|
|
|
|
int x11_width;
|
|
|
|
|
int x11_height;
|
|
|
|
|
|
2015-08-15 03:50:08 +00:00
|
|
|
|
int ndcid = 0;
|
2018-07-23 17:47:24 +00:00
|
|
|
|
void* x11_glc = NULL;
|
2015-08-19 10:45:21 +00:00
|
|
|
|
bool x11_fullscreen = false;
|
2018-07-23 17:47:24 +00:00
|
|
|
|
Atom wmDeleteMessage;
|
2015-08-19 10:45:21 +00:00
|
|
|
|
|
2015-12-13 12:47:56 +00:00
|
|
|
|
void* x11_vis;
|
|
|
|
|
|
2018-07-16 15:31:52 +00:00
|
|
|
|
extern bool dump_frame_switch;
|
|
|
|
|
|
2018-07-23 17:47:24 +00:00
|
|
|
|
void dc_stop(void);
|
2018-09-02 13:49:23 +00:00
|
|
|
|
bool dc_loadstate(void);
|
|
|
|
|
bool dc_savestate(void);
|
2018-07-23 17:47:24 +00:00
|
|
|
|
|
2015-08-19 10:45:21 +00:00
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
_NET_WM_STATE_REMOVE =0,
|
|
|
|
|
_NET_WM_STATE_ADD = 1,
|
|
|
|
|
_NET_WM_STATE_TOGGLE =2
|
|
|
|
|
};
|
2015-08-15 03:50:08 +00:00
|
|
|
|
|
2015-08-19 10:45:21 +00:00
|
|
|
|
void x11_window_set_fullscreen(bool fullscreen)
|
|
|
|
|
{
|
|
|
|
|
XEvent xev;
|
|
|
|
|
xev.xclient.type = ClientMessage;
|
2015-08-21 00:29:14 +00:00
|
|
|
|
xev.xclient.window = (Window)x11_win;
|
|
|
|
|
xev.xclient.message_type = XInternAtom((Display*)x11_disp, "_NET_WM_STATE", False);
|
2015-08-19 10:45:21 +00:00
|
|
|
|
xev.xclient.format = 32;
|
|
|
|
|
xev.xclient.data.l[0] = 2; // _NET_WM_STATE_TOGGLE
|
2015-08-21 00:29:14 +00:00
|
|
|
|
xev.xclient.data.l[1] = XInternAtom((Display*)x11_disp, "_NET_WM_STATE_FULLSCREEN", True);
|
2015-08-19 10:45:21 +00:00
|
|
|
|
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);
|
2015-08-21 00:29:14 +00:00
|
|
|
|
XSendEvent((Display*)x11_disp, DefaultRootWindow((Display*)x11_disp), False, SubstructureNotifyMask, &xev);
|
2015-08-19 10:45:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-27 14:54:15 +00:00
|
|
|
|
void event_x11_handle()
|
|
|
|
|
{
|
|
|
|
|
XEvent event;
|
|
|
|
|
|
2018-07-23 17:47:24 +00:00
|
|
|
|
while(XPending((Display *)x11_disp))
|
|
|
|
|
{
|
2018-04-27 14:54:15 +00:00
|
|
|
|
XNextEvent((Display *)x11_disp, &event);
|
|
|
|
|
|
|
|
|
|
if (event.type == ClientMessage &&
|
2018-07-23 17:47:24 +00:00
|
|
|
|
event.xclient.data.l[0] == wmDeleteMessage)
|
|
|
|
|
dc_stop();
|
2018-04-27 14:54:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-06 18:17:28 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2018-09-08 12:35:46 +00:00
|
|
|
|
kb_map[20] = 0x2D; // -
|
|
|
|
|
kb_map[21] = 0x2E; // =
|
|
|
|
|
kb_map[34] = 0x2F; // [
|
|
|
|
|
kb_map[35] = 0x30; // ]
|
2018-09-06 18:17:28 +00:00
|
|
|
|
|
2018-09-08 12:35:46 +00:00
|
|
|
|
kb_map[94] = 0x31; // \ (US) unsure of keycode
|
2018-09-06 18:17:28 +00:00
|
|
|
|
|
|
|
|
|
//32-34 "]", ";" and ":" (the 3 keys right of L)
|
2018-09-08 12:35:46 +00:00
|
|
|
|
kb_map[51] = 0x32; // ~ (non-US) *,µ in FR layout
|
|
|
|
|
kb_map[47] = 0x33; // ;
|
|
|
|
|
kb_map[48] = 0x34; // '
|
2018-09-06 18:17:28 +00:00
|
|
|
|
|
|
|
|
|
//35 hankaku/zenkaku / kanji (top left)
|
2018-09-08 12:35:46 +00:00
|
|
|
|
kb_map[49] = 0x35; // `~ (US)
|
2018-09-06 18:17:28 +00:00
|
|
|
|
|
|
|
|
|
//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
|
2018-09-08 12:35:46 +00:00
|
|
|
|
kb_map[107] = 0x46; // Print Screen
|
|
|
|
|
kb_map[78] = 0x47; // Scroll Lock
|
|
|
|
|
kb_map[127] = 0x48; // Pause
|
2018-09-06 18:17:28 +00:00
|
|
|
|
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;
|
2018-09-08 12:35:46 +00:00
|
|
|
|
//64 #| (non-US)
|
|
|
|
|
//kb_map[94] = 0x64;
|
2018-09-06 18:17:28 +00:00
|
|
|
|
//65 S3 key
|
2018-09-08 12:35:46 +00:00
|
|
|
|
//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
|
2018-09-06 18:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2018-09-18 07:27:16 +00:00
|
|
|
|
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()
|
2015-08-17 21:46:28 +00:00
|
|
|
|
{
|
2018-09-18 07:27:16 +00:00
|
|
|
|
if (empty_cursor == None)
|
2015-08-17 21:46:28 +00:00
|
|
|
|
{
|
2018-09-18 07:27:16 +00:00
|
|
|
|
Display *display = (Display*)x11_disp;
|
|
|
|
|
char data[] = { 0 };
|
2015-08-17 21:46:28 +00:00
|
|
|
|
|
2018-09-18 07:27:16 +00:00
|
|
|
|
XColor color;
|
|
|
|
|
color.red = color.green = color.blue = 0;
|
|
|
|
|
|
|
|
|
|
Pixmap pixmap = XCreateBitmapFromData(display, DefaultRootWindow(display),
|
|
|
|
|
data, 1, 1);
|
|
|
|
|
if (pixmap)
|
2015-08-17 21:46:28 +00:00
|
|
|
|
{
|
2018-09-18 07:27:16 +00:00
|
|
|
|
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
|
2018-09-26 00:00:50 +00:00
|
|
|
|
static int prev_x = -1;
|
|
|
|
|
static int prev_y = -1;
|
|
|
|
|
bool mouse_moved = false;
|
2018-09-18 07:27:16 +00:00
|
|
|
|
XEvent e;
|
|
|
|
|
|
2018-09-26 00:00:50 +00:00
|
|
|
|
Display *display = (Display*)x11_disp;
|
|
|
|
|
|
|
|
|
|
while (XCheckWindowEvent(display, (Window)x11_win,
|
2018-09-18 07:27:16 +00:00
|
|
|
|
KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask
|
|
|
|
|
| PointerMotionMask | FocusChangeMask,
|
|
|
|
|
&e))
|
|
|
|
|
{
|
|
|
|
|
switch(e.type)
|
|
|
|
|
{
|
|
|
|
|
case KeyPress:
|
|
|
|
|
case KeyRelease:
|
|
|
|
|
{
|
2018-09-26 08:08:49 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
2018-09-06 18:17:28 +00:00
|
|
|
|
// 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);
|
2018-09-08 12:35:46 +00:00
|
|
|
|
if (e.xkey.keycode == KEY_LCTRL || e.xkey.keycode == KEY_RCTRL)
|
|
|
|
|
if (e.type == KeyRelease)
|
|
|
|
|
kb_shift &= ~(0x01 | 0x10);
|
|
|
|
|
else
|
|
|
|
|
kb_shift |= (0x01 | 0x10);
|
2018-09-06 18:17:28 +00:00
|
|
|
|
|
|
|
|
|
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];
|
2018-09-26 20:58:15 +00:00
|
|
|
|
kb_key[5] = 0;
|
2018-09-06 18:17:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-18 07:27:16 +00:00
|
|
|
|
if (settings.input.DCMouse)
|
2015-08-28 02:06:36 +00:00
|
|
|
|
{
|
2018-09-18 07:27:16 +00:00
|
|
|
|
// 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();
|
|
|
|
|
}
|
2015-08-28 02:06:36 +00:00
|
|
|
|
}
|
2018-09-18 07:27:16 +00:00
|
|
|
|
if (x11_keyboard_input)
|
2018-07-16 15:31:52 +00:00
|
|
|
|
{
|
2018-09-18 07:27:16 +00:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
2018-07-16 15:31:52 +00:00
|
|
|
|
#elif FEAT_HAS_NIXPROF
|
2018-09-18 07:27:16 +00:00
|
|
|
|
else if (e.type == KeyRelease && e.xkey.keycode == KEY_F10)
|
|
|
|
|
{
|
|
|
|
|
if (sample_Switch(3000)) {
|
|
|
|
|
printf("Starting profiling\n");
|
|
|
|
|
} else {
|
|
|
|
|
printf("Stopping profiling\n");
|
|
|
|
|
}
|
2015-08-24 22:54:23 +00:00
|
|
|
|
}
|
|
|
|
|
#endif
|
2018-09-18 07:27:16 +00:00
|
|
|
|
else if (e.type == KeyRelease && e.xkey.keycode == KEY_F11)
|
2018-06-13 17:24:48 +00:00
|
|
|
|
{
|
2018-09-18 07:27:16 +00:00
|
|
|
|
x11_fullscreen = !x11_fullscreen;
|
|
|
|
|
x11_window_set_fullscreen(x11_fullscreen);
|
2018-06-13 17:24:48 +00:00
|
|
|
|
}
|
2018-09-20 17:48:46 +00:00
|
|
|
|
else if (e.type == KeyRelease && e.xkey.keycode == KEY_F2)
|
|
|
|
|
{
|
|
|
|
|
dc_savestate() ;
|
|
|
|
|
}
|
|
|
|
|
else if (e.type == KeyRelease && e.xkey.keycode == KEY_F4)
|
|
|
|
|
{
|
|
|
|
|
dc_loadstate() ;
|
|
|
|
|
}
|
2018-09-18 07:27:16 +00:00
|
|
|
|
else
|
2018-06-13 17:24:48 +00:00
|
|
|
|
{
|
2018-09-18 07:27:16 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-13 17:24:48 +00:00
|
|
|
|
if (e.type == KeyPress)
|
2018-09-18 07:27:16 +00:00
|
|
|
|
{
|
|
|
|
|
kcode[0] &= ~dc_key;
|
|
|
|
|
}
|
2018-06-13 17:24:48 +00:00
|
|
|
|
else
|
2018-09-18 07:27:16 +00:00
|
|
|
|
{
|
|
|
|
|
kcode[0] |= dc_key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(_DEBUG)
|
|
|
|
|
printf("KEY: %d -> %d: %d\n", e.xkey.keycode, dc_key, x11_dc_buttons );
|
|
|
|
|
#endif
|
2018-06-13 17:24:48 +00:00
|
|
|
|
}
|
2018-09-18 07:27:16 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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
|
2018-06-13 17:24:48 +00:00
|
|
|
|
|
2018-09-18 07:27:16 +00:00
|
|
|
|
case MotionNotify:
|
2018-09-26 00:00:50 +00:00
|
|
|
|
if (settings.input.DCMouse)
|
2018-09-18 07:27:16 +00:00
|
|
|
|
{
|
2018-09-26 00:00:50 +00:00
|
|
|
|
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;
|
2018-09-18 07:27:16 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2015-08-17 21:46:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-26 00:00:50 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2015-08-15 03:50:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void input_x11_init()
|
|
|
|
|
{
|
2018-06-13 17:24:48 +00:00
|
|
|
|
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;
|
2015-08-15 03:50:08 +00:00
|
|
|
|
|
2018-06-13 17:24:48 +00:00
|
|
|
|
// 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;
|
2015-08-15 03:50:08 +00:00
|
|
|
|
|
2018-07-18 17:31:35 +00:00
|
|
|
|
// Used by some "arcade" controllers
|
2018-06-13 17:24:48 +00:00
|
|
|
|
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;
|
2015-08-15 03:50:08 +00:00
|
|
|
|
|
2018-07-18 17:31:35 +00:00
|
|
|
|
// Shoulder trigger
|
|
|
|
|
x11_keymap[KEY_F] = DC_AXIS_LT;
|
|
|
|
|
x11_keymap[KEY_V] = DC_AXIS_RT;
|
|
|
|
|
|
2018-08-12 13:17:56 +00:00
|
|
|
|
x11_keyboard_input = (cfgLoadInt("input", "enable_x11_keyboard", 1) >= 1);
|
|
|
|
|
if (!x11_keyboard_input)
|
|
|
|
|
printf("X11 Keyboard input disabled by config.\n");
|
2018-09-06 18:17:28 +00:00
|
|
|
|
|
|
|
|
|
init_kb_map();
|
2015-08-15 03:50:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void x11_window_create()
|
|
|
|
|
{
|
2015-08-17 21:46:28 +00:00
|
|
|
|
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
|
2015-08-20 15:15:06 +00:00
|
|
|
|
x11Display = XOpenDisplay(NULL);
|
2015-08-17 21:46:28 +00:00
|
|
|
|
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);
|
2018-07-23 17:47:24 +00:00
|
|
|
|
printf("Chosen visual ID = 0x%lx\n", vi->visualid);
|
2015-08-17 21:46:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2018-09-18 07:27:16 +00:00
|
|
|
|
if (settings.input.DCMouse)
|
|
|
|
|
sWA.event_mask |= PointerMotionMask | FocusChangeMask;
|
2015-08-17 21:46:28 +00:00
|
|
|
|
ui32Mask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap;
|
|
|
|
|
|
2015-12-13 12:47:56 +00:00
|
|
|
|
x11_width = cfgLoadInt("x11", "width", DEFAULT_WINDOW_WIDTH);
|
|
|
|
|
x11_height = cfgLoadInt("x11", "height", DEFAULT_WINDOW_HEIGHT);
|
2015-08-21 00:16:48 +00:00
|
|
|
|
x11_fullscreen = (cfgLoadInt("x11", "fullscreen", DEFAULT_FULLSCREEN) > 0);
|
2015-08-17 21:46:28 +00:00
|
|
|
|
|
2015-08-19 10:45:21 +00:00
|
|
|
|
if (x11_width < 0 || x11_height < 0)
|
2015-08-17 21:46:28 +00:00
|
|
|
|
{
|
2015-08-19 10:45:21 +00:00
|
|
|
|
x11_width = XDisplayWidth(x11Display, x11Screen);
|
|
|
|
|
x11_height = XDisplayHeight(x11Display, x11Screen);
|
2015-08-17 21:46:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Creates the X11 window
|
2015-08-19 10:45:21 +00:00
|
|
|
|
x11Window = XCreateWindow(x11Display, RootWindow(x11Display, x11Screen), (ndcid%3)*640, (ndcid/3)*480, x11_width, x11_height,
|
2015-08-17 21:46:28 +00:00
|
|
|
|
0, depth, InputOutput, x11Visual->visual, ui32Mask, &sWA);
|
|
|
|
|
|
2018-07-23 17:47:24 +00:00
|
|
|
|
// Capture the close window event
|
2018-04-27 14:54:15 +00:00
|
|
|
|
wmDeleteMessage = XInternAtom(x11Display, "WM_DELETE_WINDOW", False);
|
|
|
|
|
XSetWMProtocols(x11Display, x11Window, &wmDeleteMessage, 1);
|
|
|
|
|
|
2015-08-19 10:45:21 +00:00
|
|
|
|
if(x11_fullscreen)
|
|
|
|
|
{
|
|
|
|
|
|
2015-08-17 21:46:28 +00:00
|
|
|
|
// 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);
|
2015-08-19 10:45:21 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-08-17 21:46:28 +00:00
|
|
|
|
XMapWindow(x11Display, x11Window);
|
2015-08-19 10:45:21 +00:00
|
|
|
|
}
|
2015-08-17 21:46:28 +00:00
|
|
|
|
|
2015-08-21 00:07:03 +00:00
|
|
|
|
#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
|
|
|
|
|
|
2015-08-17 21:46:28 +00:00
|
|
|
|
XFlush(x11Display);
|
|
|
|
|
|
|
|
|
|
//(EGLNativeDisplayType)x11Display;
|
|
|
|
|
x11_disp = (void*)x11Display;
|
|
|
|
|
x11_win = (void*)x11Window;
|
2015-12-13 12:47:56 +00:00
|
|
|
|
x11_vis = (void*)x11Visual->visual;
|
2018-07-06 20:38:48 +00:00
|
|
|
|
|
|
|
|
|
x11_window_set_text("Reicast");
|
2015-08-17 21:46:28 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
printf("Not creating X11 window ..\n");
|
|
|
|
|
}
|
2015-08-15 03:50:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void x11_window_set_text(const char* text)
|
|
|
|
|
{
|
2015-08-17 21:46:28 +00:00
|
|
|
|
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));
|
|
|
|
|
}
|
2015-08-15 03:50:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-27 14:54:15 +00:00
|
|
|
|
void x11_gl_context_destroy()
|
|
|
|
|
{
|
|
|
|
|
glXMakeCurrent((Display*)x11_disp, None, NULL);
|
|
|
|
|
glXDestroyContext((Display*)x11_disp, x11_glc);
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-15 03:50:08 +00:00
|
|
|
|
void x11_window_destroy()
|
|
|
|
|
{
|
2015-08-17 21:46:28 +00:00
|
|
|
|
// close XWindow
|
|
|
|
|
if (x11_win)
|
|
|
|
|
{
|
2015-08-21 00:29:14 +00:00
|
|
|
|
XDestroyWindow((Display*)x11_disp, (Window)x11_win);
|
2018-07-23 17:47:24 +00:00
|
|
|
|
x11_win = NULL;
|
2015-08-17 21:46:28 +00:00
|
|
|
|
}
|
|
|
|
|
if (x11_disp)
|
|
|
|
|
{
|
2018-08-20 11:17:01 +00:00
|
|
|
|
#if !defined(GLES)
|
2018-07-23 17:47:24 +00:00
|
|
|
|
if (x11_glc)
|
|
|
|
|
{
|
|
|
|
|
glXMakeCurrent((Display*)x11_disp, None, NULL);
|
|
|
|
|
glXDestroyContext((Display*)x11_disp, (GLXContext)x11_glc);
|
|
|
|
|
x11_glc = NULL;
|
|
|
|
|
}
|
2018-08-20 11:17:01 +00:00
|
|
|
|
#endif
|
2015-08-21 00:29:14 +00:00
|
|
|
|
XCloseDisplay((Display*)x11_disp);
|
2018-07-23 17:47:24 +00:00
|
|
|
|
x11_disp = NULL;
|
2015-08-17 21:46:28 +00:00
|
|
|
|
}
|
2015-08-15 03:50:08 +00:00
|
|
|
|
}
|
2015-08-17 21:46:28 +00:00
|
|
|
|
#endif
|