diff --git a/src/Makefile b/src/Makefile index c77470bc..98ee39c5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -586,6 +586,11 @@ endif ifeq ($(API),win32) APIOBJ = win32screen.o endif +ifeq ($(API),x11) + APIOBJ = x11screen.o + COPT += -DUSE_X11 + LOPT += -lX11 +endif #To enable Joystick emulation of cursor, make USE_JOYSTICK=1 (for input.o) #This can be necessary to test cursor code on a PC, but by default for all diff --git a/src/input.c b/src/input.c index 516e4df6..ecab1d0d 100644 --- a/src/input.c +++ b/src/input.c @@ -31,6 +31,13 @@ #include #endif +#ifdef USE_X11 +#include +#include +#include +#include +#endif + #include "global.h" #include "keyboard.h" #include "screen.h" @@ -41,6 +48,9 @@ #include "input.h" #include "loadsave.h" +#ifdef USE_X11 +extern Display * X11_display; +#endif #if defined(USE_SDL) #define RSUPER_EMULATES_META_MOD @@ -1215,6 +1225,146 @@ int Get_input(int sleep_time) WaitMessage(); KillTimer(NULL, timerId); } +#elif defined(USE_X11) + int user_feedback_required = 0; // Flag qui indique si on doit arrêter de traiter les évènements ou si on peut enchainer + + Color_cycling(); + // Commit any pending screen update. + // This is done in this function because it's called after reading + // some user input. + Flush_update(); + + Key_ANSI = 0; + Key_UNICODE = 0; + Key = 0; + Mouse_moved=0; + Input_new_mouse_X = Mouse_X; + Input_new_mouse_Y = Mouse_Y; + Input_new_mouse_K = Mouse_K; + + XFlush(X11_display); + while(!user_feedback_required && XPending(X11_display) > 0) + { + word mod = 0; + XEvent event; + XNextEvent(X11_display, &event); + switch(event.type) + { + case KeyPress: + { + KeySym sym; + //printf("key code = %d state=0x%08x\n", event.xkey.keycode, event.xkey.state); + // right/left window 40 Mod4Mask + // left alt = 8 Mod1Mask + // right alt = 80 Mod5Mask + // NumLock = 10 Mod2Mask + if (event.xkey.state & ShiftMask) + mod |= MOD_SHIFT; + if (event.xkey.state & ControlMask) + mod |= MOD_CTRL; + if (event.xkey.state & (Mod1Mask | Mod5Mask)) + mod |= MOD_ALT; + if (event.xkey.state & Mod3Mask) + mod |= MOD_META; + //sym = XKeycodeToKeysym(X11_display, event.xkey.keycode, 0); + sym = XkbKeycodeToKeysym(X11_display, event.xkey.keycode, 0, 0); + //printf("sym = %04lx %s\t\tmod=%04x\n", sym, XKeysymToString(sym), mod); + Key = mod | (sym & 0x0fff); + //sym = XkbKeycodeToKeysym(X11_display, event.xkey.keycode, 0, event.xkey.state); + if ((sym & 0xf000) != 0xf000) // test for standard key + { + int count; + char buffer[16]; + static XComposeStatus status; + count = XLookupString(&event.xkey, buffer, sizeof(buffer), + &sym, &status); + //printf(" sym = %04lx %s %d %s\n", sym, XKeysymToString(sym), count, buffer); + Key_UNICODE = sym; + if (sym < 0x100) + Key_ANSI = sym; + } + user_feedback_required = 1; + } + break; + case ButtonPress: // left = 1, middle = 2, right = 3, wheelup = 4, wheeldown = 5 + //printf("Press button = %d state = 0x%08x\n", event.xbutton.button, event.xbutton.state); + if (event.xkey.state & ShiftMask) + mod |= MOD_SHIFT; + if (event.xkey.state & ControlMask) + mod |= MOD_CTRL; + if (event.xkey.state & (Mod1Mask | Mod5Mask)) + mod |= MOD_ALT; + if (event.xkey.state & Mod3Mask) + mod |= MOD_META; + switch(event.xbutton.button) + { + case 1: + case 3: + { + byte mask = 1; + if(event.xbutton.button == 3) + mask ^= 3; + if (Button_inverter) + mask ^= 3; + Input_new_mouse_K |= mask; + user_feedback_required = Move_cursor_with_constraints(); + } + break; + case 2: + Key = KEY_MOUSEMIDDLE | mod; + user_feedback_required = 1; + break; + case 4: + Key = KEY_MOUSEWHEELUP | mod; + user_feedback_required = 1; + break; + case 5: + Key = KEY_MOUSEWHEELDOWN | mod; + user_feedback_required = 1; + break; + } + break; + case ButtonRelease: + //printf("Release button = %d\n", event.xbutton.button); + if(event.xbutton.button == 1 || event.xbutton.button == 3) + { + byte mask = 1; + if(event.xbutton.button == 3) + mask ^= 3; + if (Button_inverter) + mask ^= 3; + Input_new_mouse_K &= ~mask; + user_feedback_required = Move_cursor_with_constraints(); + } + break; + case MotionNotify: + //printf("mouse %dx%d\n", event.xmotion.x, event.xmotion.y); + Input_new_mouse_X = (event.xmotion.x < 0) ? 0 : event.xmotion.x/Pixel_width; + Input_new_mouse_Y = (event.xmotion.y < 0) ? 0 : event.xmotion.y/Pixel_height; + user_feedback_required = Move_cursor_with_constraints(); + break; + case Expose: + printf("Expose (%d,%d) (%d,%d)\n", event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height); + Update_rect(event.xexpose.x, event.xexpose.y, + event.xexpose.width, event.xexpose.height); + break; + default: + printf("event.type = %d\n", event.type); + } + } + // If the cursor was moved since last update, + // it was erased, so we need to redraw it (with the preview brush) + if (Mouse_moved) + { + Compute_paintbrush_coordinates(); + Display_cursor(); + return 1; + } + if (user_feedback_required) + return 1; + // Nothing significant happened + if (sleep_time) + usleep(1000 * sleep_time); #endif return 0; } diff --git a/src/keyboard.c b/src/keyboard.c index c5dcb74c..5971d1a3 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -810,12 +810,14 @@ word Key_for_scancode(word scancode) word Get_Key_modifiers(void) { word mod = 0; +#if defined(WIN32) if (GetKeyState(VK_SHIFT) & 0x8000) mod |= MOD_SHIFT; if (GetKeyState(VK_CONTROL) & 0x8000) mod |= MOD_CTRL; if (GetKeyState(VK_MENU) & 0x8000) mod |= MOD_ALT; +#endif return mod; } #endif diff --git a/src/keycodes.h b/src/keycodes.h index e44a03f3..c67b669e 100644 --- a/src/keycodes.h +++ b/src/keycodes.h @@ -26,12 +26,16 @@ #include #elif defined(WIN32) #include +#elif defined(USE_X11) +#include #endif #if defined(USE_SDL) #define K2K(x) (x) #elif defined(USE_SDL2) #define K2K(x) ((((x) & 0x40000000) >> 19) | ((x) & 0x1FF)) +#elif defined(USE_X11) +#define K2K(x) ((x) & 0x0FFF) #endif /* generated lists */ @@ -150,6 +154,107 @@ #define KEY_F11 K2K(SDLK_F11) #define KEY_F12 K2K(SDLK_F12) // end of KEY definitions for SDL and SDL2 +#elif defined(USE_X11) +// KEY definitions for x11 +#define KEY_UNKNOWN 0 +#define KEY_ESCAPE K2K(XK_Escape) +#define KEY_RETURN K2K(XK_Return) +#define KEY_BACKSPACE K2K(XK_BackSpace) +#define KEY_TAB K2K(XK_Tab) +#define KEY_UP K2K(XK_Up) +#define KEY_DOWN K2K(XK_Down) +#define KEY_LEFT K2K(XK_Left) +#define KEY_RIGHT K2K(XK_Right) +#define KEY_LEFTBRACKET K2K(XK_bracketleft) +#define KEY_RIGHTBRACKET K2K(XK_bracketright) +#define KEY_INSERT K2K(XK_Insert) +#define KEY_DELETE K2K(XK_Delete) +#define KEY_COMMA K2K(XK_comma) +#define KEY_BACKQUOTE K2K(XK_grave) +#define KEY_PAGEUP K2K(XK_Page_Up) +#define KEY_PAGEDOWN K2K(XK_Page_Down) +#define KEY_HOME K2K(XK_Home) +#define KEY_END K2K(XK_End) +#define KEY_KP_PLUS K2K(XK_KP_Add) +#define KEY_KP_MINUS K2K(XK_KP_Subtract) +#define KEY_KP_MULTIPLY K2K(XK_KP_Multiply) +#define KEY_KP_ENTER K2K(XK_KP_Enter) +#define KEY_KP_DIVIDE K2K(XK_KP_Divide) +#define KEY_KP_PERIOD K2K(XK_KP_Decimal) +#define KEY_KP_EQUALS K2K(XK_KP_Equal) +#define KEY_EQUALS K2K(XK_equal) +#define KEY_MINUS K2K(XK_minus) +#define KEY_PERIOD K2K(XK_period) +#define KEY_CAPSLOCK K2K(XK_Caps_Lock) +#define KEY_CLEAR K2K(XK_Clear) +#define KEY_SPACE K2K(XK_space) +#define KEY_PAUSE K2K(XK_Pause) +#define KEY_LSHIFT K2K(XK_Shift_L) +#define KEY_RSHIFT K2K(XK_Shift_R) +#define KEY_LCTRL K2K(XK_Control_L) +#define KEY_RCTRL K2K(XK_Control_R) +#define KEY_LALT K2K(XK_Alt_L) +#define KEY_RALT K2K(XK_Alt_R) +#define KEY_0 K2K(XK_0) +#define KEY_1 K2K(XK_1) +#define KEY_2 K2K(XK_2) +#define KEY_3 K2K(XK_3) +#define KEY_4 K2K(XK_4) +#define KEY_5 K2K(XK_5) +#define KEY_6 K2K(XK_6) +#define KEY_7 K2K(XK_7) +#define KEY_8 K2K(XK_8) +#define KEY_9 K2K(XK_9) +#define KEY_a K2K(XK_a) +#define KEY_b K2K(XK_b) +#define KEY_c K2K(XK_c) +#define KEY_d K2K(XK_d) +#define KEY_e K2K(XK_e) +#define KEY_f K2K(XK_f) +#define KEY_g K2K(XK_g) +#define KEY_h K2K(XK_h) +#define KEY_i K2K(XK_i) +#define KEY_j K2K(XK_j) +#define KEY_k K2K(XK_k) +#define KEY_l K2K(XK_l) +#define KEY_m K2K(XK_m) +#define KEY_n K2K(XK_n) +#define KEY_o K2K(XK_o) +#define KEY_p K2K(XK_p) +#define KEY_q K2K(XK_q) +#define KEY_r K2K(XK_r) +#define KEY_s K2K(XK_s) +#define KEY_t K2K(XK_t) +#define KEY_u K2K(XK_u) +#define KEY_v K2K(XK_v) +#define KEY_w K2K(XK_w) +#define KEY_x K2K(XK_x) +#define KEY_y K2K(XK_y) +#define KEY_z K2K(XK_z) +#define KEY_KP0 K2K(XK_KP_0) +#define KEY_KP1 K2K(XK_KP_1) +#define KEY_KP2 K2K(XK_KP_2) +#define KEY_KP3 K2K(XK_KP_3) +#define KEY_KP4 K2K(XK_KP_4) +#define KEY_KP5 K2K(XK_KP_5) +#define KEY_KP6 K2K(XK_KP_6) +#define KEY_KP7 K2K(XK_KP_7) +#define KEY_KP8 K2K(XK_KP_8) +#define KEY_KP9 K2K(XK_KP_9) +#define KEY_SCROLLOCK K2K(XK_Scroll_Lock) +#define KEY_F1 K2K(XK_F1) +#define KEY_F2 K2K(XK_F2) +#define KEY_F3 K2K(XK_F3) +#define KEY_F4 K2K(XK_F4) +#define KEY_F5 K2K(XK_F5) +#define KEY_F6 K2K(XK_F6) +#define KEY_F7 K2K(XK_F7) +#define KEY_F8 K2K(XK_F8) +#define KEY_F9 K2K(XK_F9) +#define KEY_F10 K2K(XK_F10) +#define KEY_F11 K2K(XK_F11) +#define KEY_F12 K2K(XK_F12) +// end of KEY definitions for x11 #elif defined(WIN32) // KEY definitions for win32 #define KEY_UNKNOWN 0 diff --git a/src/x11screen.c b/src/x11screen.c new file mode 100644 index 00000000..e164d9b2 --- /dev/null +++ b/src/x11screen.c @@ -0,0 +1,256 @@ +/* vim:expandtab:ts=2 sw=2: +*/ +/* Grafx2 - The Ultimate 256-color bitmap paint program + + Copyright 2018 Thomas Bernard + Copyright 2008 Yves Rizoud + Copyright 2007 Adrien Destugues + Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud) + + Grafx2 is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 + of the License. + + Grafx2 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grafx2; if not, see +*/ +#include +#include +#include +#include +#include +#include "screen.h" +#include "gfx2surface.h" + +Display * X11_display = NULL; +static Window X11_window = 0; +static XImage * X11_image = NULL; +static char * image_pixels = NULL; +static XTextProperty windowName; +static GC X11_gc = 0; +static T_GFX2_Surface * screen = NULL; + +void GFX2_Set_mode(int *width, int *height, int fullscreen) +{ + int s; + int depth; + unsigned long white, black; + char * winName[] = { "GrafX2" }; + Visual * visual; + const char blank_data[1] = { 0 }; + Pixmap blank; + Cursor cursor; + XColor dummy; + + if (X11_display == NULL) + X11_display = XOpenDisplay(NULL);//getenv("DISPLAY") + if (X11_display == NULL) + { + fprintf(stderr, "X11: cannot open display\n"); + exit(1); + } + s = DefaultScreen(X11_display); + black = BlackPixel(X11_display, s); + white = WhitePixel(X11_display, s); + visual = DefaultVisual(X11_display, s); + + { + int i; + int count = 0; + int * depths = XListDepths(X11_display, s, &count); + printf("DefaultDepth = %d, DisplayPlanes = %d\n", DefaultDepth(X11_display, s), DisplayPlanes(X11_display, s)); + if (depths != NULL) + { + for (i = 0; i < count; i++) + printf(" %d", depths[i]); + printf("\n"); + XFree(depths); + } + } + depth = DisplayPlanes(X11_display, s); + + X11_window = XCreateSimpleWindow(X11_display, RootWindow(X11_display, s), + 0, 0, *width, *height, 0, white, black); + + // create blank 1x1 pixmap to make a 1x1 transparent cursor + blank = XCreateBitmapFromData(X11_display, X11_window, blank_data, 1, 1); + cursor = XCreatePixmapCursor(X11_display, blank, blank, &dummy, &dummy, 0, 0); + //cursor = XCreateFontCursor(X11_display, 130 /*XC_tcross*/); + XDefineCursor(X11_display, X11_window, cursor); + XFreePixmap(X11_display, blank); + XFreeCursor(X11_display, cursor); + + X11_gc = XCreateGC(X11_display, X11_window, 0, NULL); + XSetFunction(X11_display, X11_gc, GXcopy); + + XStringListToTextProperty(winName, 1, &windowName); + XSetWMName(X11_display, X11_window, &windowName); + // TODO : set icon + + screen = New_GFX2_Surface(*width, *height); + memset(screen->pixels, 0, *width * *height); + + image_pixels = malloc(*height * *width * 4); + memset(image_pixels, 64, *height * *width * 4); +#if 0 +{ +int i; +for (i= 3*8; i < (*height * *width * 4); i += *width * 4) +{ +image_pixels[i+0] = 0; // B +image_pixels[i+1] = 0; // G +image_pixels[i+2] = 0; // R +} +} +#endif + X11_image = XCreateImage(X11_display, visual, depth, + ZPixmap, 0, image_pixels, *width, *height, + 32, 0/**width * 4*/); + if(X11_image == NULL) + { + fprintf(stderr, "XCreateImage failed\n"); + exit(1); + } + + XSelectInput(X11_display, X11_window, + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | ExposureMask | StructureNotifyMask); + + XMapWindow(X11_display, X11_window); + XFlush(X11_display); +} + +byte Get_Screen_pixel(int x, int y) +{ + if(screen == NULL) return 0; + return screen->pixels[x + y * screen->w]; +} + +void Set_Screen_pixel(int x, int y, byte value) +{ + if(screen == NULL) return; + screen->pixels[x + y * screen->w] = value; +} + +byte* Get_Screen_pixel_ptr(int x, int y) +{ + if(screen == NULL) return NULL; + return screen->pixels + x + y * screen->w; +} + +void Screen_FillRect(int x, int y, int w, int h, byte color) +{ + int i; + byte * ptr; + + if (x < 0) + { + w += x; + x = 0; + } + if (y < 0) + { + h += y; + y = 0; + } + if (x > screen->w || y > screen->h) + return; + if ((x + w) > screen->w) + w = screen->w - x; + if ((y + h) > screen->h) + h = screen->h - y; + if (w <= 0 || h <= 0) + return; + for (i = 0; i < h; i++) + { + ptr = Get_Screen_pixel_ptr(x, y + i); + memset(ptr, color, w); + } +} + +int SetPalette(const T_Components * colors, int firstcolor, int ncolors) +{ + if (screen == NULL) return 0; + memcpy(screen->palette + firstcolor, colors, ncolors * sizeof(T_Components)); + return 1; +} + +void Update_rect(short x, short y, unsigned short width, unsigned short height) +{ + int line, i; + if (screen == NULL || X11_image == NULL) return; + x *= Pixel_width; + width *= Pixel_width; + y *= Pixel_height; + height *= Pixel_height; +//printf("Update_rect(%d %d %d %d) %d %d\n", x, y, width, height, screen->w, screen->h); + if (y >= screen->h || x >= screen->w) return; + if (y + height > screen->h) + height = screen->h - y; + if (x + width > screen->w) + width = screen->w - x; + for (line = y; line < y + height; line++) + { +#if 0 + const byte * src = Get_Screen_pixel_ptr(x, line); + byte * dest = image_pixels + line * X11_image->bytes_per_line + x * 4, + i = width; + do + { + dest[0] = screen->palette[*src].B; + dest[1] = screen->palette[*src].G; + dest[2] = screen->palette[*src].R; + dest[3] = 0; + src++; + dest += 4; + } + while(--i > 0); +#else + for (i = 0; i < width; i++) + { + byte v = Get_Screen_pixel(x + i, line); + XPutPixel(X11_image, x + i, line, + (unsigned)screen->palette[v].R << 16 | (unsigned)screen->palette[v].G << 8 | (unsigned)screen->palette[v].B); + } +#endif + } + XPutImage(X11_display, X11_window, X11_gc, X11_image, + x, y, x, y, width, height); + //XPutImage(X11_display, X11_window, X11_gc, X11_image, + // 0, 0, 0, 0, X11_image->width, X11_image->height); + //XSync(X11_display, False); +} + +void Flush_update(void) +{ + if (X11_display != NULL) + XFlush(X11_display); +} + +void Update_status_line(short char_pos, short width) +{ + Update_rect((18+char_pos*8)*Menu_factor_X*Pixel_width, Menu_status_Y*Pixel_height, + width*8*Menu_factor_X*Pixel_width, 8*Menu_factor_Y*Pixel_height); +} + +void Clear_border(byte color) +{ +(void)color;//TODO +} + +volatile int Allow_colorcycling = 0; + +/// Activates or desactivates file drag-dropping in program window. +void Allow_drag_and_drop(int flag) +{ +(void)flag; +} + +void Define_icon(void) +{ +} diff --git a/tools/generate_keycodes.py b/tools/generate_keycodes.py index e0697ac2..ae842d42 100755 --- a/tools/generate_keycodes.py +++ b/tools/generate_keycodes.py @@ -42,6 +42,31 @@ win32vk = { 'RALT': 'RMENU', } +x11xk = { +'BACKSPACE': 'BackSpace', +'CAPSLOCK': 'Caps_Lock', +'PAGEUP': 'Page_Up', +'PAGEDOWN': 'Page_Down', +'BACKQUOTE': 'grave', +'KP_PLUS': 'KP_Add', +'KP_MINUS': 'KP_Subtract', +'KP_EQUALS': 'KP_Equal', +'KP_PERIOD': 'KP_Decimal', +'COMMA': 'comma', +'SPACE': 'space', +'EQUALS': 'equal', +'MINUS': 'minus', +'PERIOD': 'period', +'LEFTBRACKET': 'bracketleft', +'RIGHTBRACKET': 'bracketright', +'LCTRL': 'Control_L', +'RCTRL': 'Control_R', +'LALT': 'Alt_L', +'RALT': 'Alt_R', +'LSHIFT': 'Shift_L', +'RSHIFT': 'Shift_R', +} + def keycode_def(section, key, index, native_key=None): if native_key is None: native_key = key @@ -56,6 +81,13 @@ def keycode_def(section, key, index, native_key=None): return '#define KEY_%-12s 0\n' % (key) else: return '#define KEY_%-12s VK_%s\n' % (key, native_key) + elif section == 'x11': + if native_key[0:3] == 'Kp_': + native_key = 'KP_' + native_key[3:] + if key == 'UNKNOWN': + return '#define KEY_%-12s 0\n' % (key) + else: + return '#define KEY_%-12s K2K(XK_%s)\n' % (key, native_key) else: return '#define KEY_%-12s %d\n' % (key, index) @@ -65,6 +97,11 @@ def add_keycodes_defs(section, lines): for key in keys: if section == 'win32' and key in win32vk: lines.append(keycode_def(section, key, i, win32vk[key])) + elif section == 'x11': + if key in x11xk: + lines.append(keycode_def(section, key, i, x11xk[key])) + else: + lines.append(keycode_def(section, key, i, key.title())) else: lines.append(keycode_def(section, key, i)) i = i + 1 @@ -80,11 +117,15 @@ def add_keycodes_defs(section, lines): key = "KP%d" % (j) if section == 'win32': lines.append(keycode_def(section, key, i, "NUMPAD%d" % (j))) + elif section == 'x11': + lines.append(keycode_def(section, key, i, "KP_%d" % (j))) else: lines.append(keycode_def(section, key, i)) i = i + 1 if section == 'win32': lines.append(keycode_def(section, 'SCROLLOCK', i, 'SCROLL')) + elif section == 'x11': + lines.append(keycode_def(section, 'SCROLLOCK', i, 'Scroll_Lock')) else: lines.append(keycode_def(section, 'SCROLLOCK', i)) i = i + 1