// Written in the D programming language. /** This module contains implementation of Win32 platform support Provides XCB platform and window classes. Usually you don't need to use this module directly. NOTE: Current implementation of XCB backend does not work well. Consider using of SDL2 backend instead. Synopsis: ---- import dlangui.platforms.x11.x11app; ---- Copyright: Vadim Lopatin, 2014 License: Boost License 1.0 Authors: Vadim Lopatin, coolreader.org@gmail.com */ module src.dlangui.platforms.x11.x11app; version(USE_XCB): import std.string; import std.c.linux.X11.xcb.xcb; import std.c.linux.X11.xcb.shm; import std.c.linux.X11.xcb.xproto; import std.c.linux.X11.xcb.image; import std.c.linux.X11.keysymdef; import std.c.linux.linux; import std.c.stdlib; import std.conv; import dlangui.core.logger; import dlangui.core.events; import dlangui.core.files; import dlangui.graphics.drawbuf; import dlangui.graphics.fonts; import dlangui.graphics.ftfonts; import dlangui.graphics.resources; import dlangui.widgets.styles; import dlangui.platforms.common.platform; version (USE_OPENGL) { import dlangui.graphics.glsupport; } import derelict.opengl3.gl3; import derelict.opengl3.glx; // pragma(lib, "xcb"); // pragma(lib, "xcb-shm"); // pragma(lib, "xcb-image"); // pragma(lib, "X11-xcb"); // pragma(lib, "X11"); // pragma(lib, "dl"); extern (System) xcb_connection_t *XGetXCBConnection(std.c.linux.X11.Xlib.Display *dpy); enum XEventQueueOwner { XlibOwnsEventQueue = 0, XCBOwnsEventQueue }; extern (System) void XSetEventQueueOwner(std.c.linux.X11.Xlib.Display *dpy, XEventQueueOwner owner); struct xcb_key_symbols_t {}; /* enumeration for col parameter? */ enum xcb_lookup_t { xcb_lookup_none_t = 1, xcb_lookup_chars_t = 2, xcb_lookup_key_sym_t = 3, xcb_lookup_both_t = 4 }; extern(C) xcb_key_symbols_t *xcb_key_symbols_alloc (xcb_connection_t *c); extern(C) void xcb_key_symbols_free (xcb_key_symbols_t *syms); extern(C) xcb_keysym_t xcb_key_symbols_get_keysym (xcb_key_symbols_t *syms, xcb_keycode_t keycode, int col); extern(C) xcb_keysym_t xcb_key_press_lookup_keysym (xcb_key_symbols_t *syms, xcb_key_press_event_t *event, int col); extern(C) xcb_keysym_t xcb_key_release_lookup_keysym (xcb_key_symbols_t *syms, xcb_key_release_event_t *event, int col); class XCBWindow : Window { xcb_window_t _w; xcb_gcontext_t _g; xcb_image_t * _image; xcb_shm_segment_info_t shminfo; version(USE_OPENGL) { /* Create GLX Window */ private GLXDrawable _drawable; private GLXWindow _glxwindow; private GLXContext _context; private GLXFBConfig _fb_config; } private int _visualID = 0; private xcb_colormap_t _colormap; @property xcb_window_t windowId() { return _w; } this(string caption, Window parent) { _caption = caption; Log.d("Creating XCB window"); create(); } ~this() { Log.d("Destroying window"); } bool create() { import std.c.linux.X11.Xlib; uint mask; uint[3] values; // disable opengl for testing _enableOpengl = false; /* create black graphics context */ if (true || !_enableOpengl) { _g = xcb_generate_id(_xcbconnection); _w = _xcbscreen.root; mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES; values[0] = _xcbscreen.black_pixel; values[1] = 0; xcb_create_gc(_xcbconnection, _g, _w, mask, &values[0]); } ubyte depth = _xcbscreen.root_depth; /* create window */ _w = xcb_generate_id(_xcbconnection); Log.d("window=", _w, " gc=", _g); version (USE_OPENGL) { if (_enableOpengl) { int[] visual_attribs = [ GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_DOUBLEBUFFER, 1, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, std.c.linux.X11.Xlib.None]; Log.d("Getting framebuffer config"); int fbcount; GLXFBConfig *fbc = glXChooseFBConfig(_display, DefaultScreen(_display), visual_attribs.ptr, &fbcount); if (!fbc) { Log.d("Failed to retrieve a framebuffer config"); //return 1; } Log.d("Getting XVisualInfo"); _fb_config = fbc[0]; auto vi = glXGetVisualFromFBConfig(_display, _fb_config); //auto vi = glXChooseVisual(_display, std.c.linux.X11.Xlib.DefaultScreen(_display), attributeList.ptr); _visualID = vi.visualid; //swa.colormap = std.c.linux.X11.Xlib.XCreateColormap(_display, std.c.linux.X11.Xlib.RootWindow(_display, vi.screen), vi.visual, 0); // AllocNone Log.d("Creating color map"); _colormap = xcb_generate_id(_xcbconnection); /* Create colormap */ xcb_create_colormap( _xcbconnection, XCB_COLORMAP_ALLOC_NONE, _colormap, _xcbscreen.root, _visualID ); depth = cast(ubyte)vi.depth; } } //mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; //values[0] = _xcbscreen.white_pixel; int visualId; uint eventmask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_VISIBILITY_CHANGE; if (_enableOpengl) { mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; values[0] = _xcbscreen.white_pixel; values[1] = eventmask; values[2] = _colormap; //visualId = _xcbscreen.root_visual; visualId = _visualID; } else { mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; values[0] = _xcbscreen.white_pixel; values[1] = eventmask; visualId = _xcbscreen.root_visual; } Log.d("xcb_create_window - window=", _w, " VisualID=", _visualID); auto res = xcb_create_window(_xcbconnection, depth, //_xcbscreen.root_depth, //XCB_COPY_FROM_PARENT,//_xcbscreen.root_depth, _w, _xcbscreen.root, 30, 30, 800, 650, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, visualId, mask, &values[0]); xcb_flush(_xcbconnection); windowCaption = _caption; return true; } private int _imageDx; private int _imageDy; void createImage() { if (_image) { if (_imageDx == _imageDx && _imageDy == _dy) return; // already have image of proper size Log.i("CRXCBScreen::createImage - destroying existing image"); xcb_image_destroy(_image); _image = null; } _imageDx = _dx; _imageDy = _dy; Log.i("CRXCBScreen::createImage ", _dx, "x", _dy); xcb_shm_query_version_reply_t * rep_shm; rep_shm = xcb_shm_query_version_reply (_xcbconnection, xcb_shm_query_version(_xcbconnection), null); if(rep_shm) { xcb_image_format_t format; int shmctl_status; if (rep_shm.shared_pixmaps && (rep_shm.major_version > 1 || rep_shm.minor_version > 0)) format = cast(xcb_image_format_t)rep_shm.pixmap_format; else format = XCB_IMAGE_FORMAT_Z_PIXMAP; _image = xcb_image_create_native (_xcbconnection, cast(short)_dx, cast(short)_dy, format, _xcbscreendepth, null, ~0, null); //format, depth, NULL, ~0, NULL); //format, depth, NULL, ~0, NULL); assert(_image); shminfo.shmid = shmget (IPC_PRIVATE, _image.stride*_image.height, IPC_CREAT | octal!777); assert(shminfo.shmid != cast(uint)-1); shminfo.shmaddr = cast(ubyte*)shmat (shminfo.shmid, null, 0); assert(shminfo.shmaddr); _image.data = shminfo.shmaddr; Log.d("Created image depth=", _image.depth, " bpp=", _image.bpp, " stride=", _image.stride ); shminfo.shmseg = xcb_generate_id (_xcbconnection); xcb_shm_attach (_xcbconnection, shminfo.shmseg, shminfo.shmid, 0); shmctl_status = shmctl(shminfo.shmid, IPC_RMID, null); assert(shmctl_status != -1); free(rep_shm); } else { Log.e("Can't get shms"); } } void draw(ColorDrawBuf buf) { int i; i = xcb_image_shm_get(_xcbconnection, _w, _image, shminfo, 0, 0, XCB_ALL_PLANES); if (!i) { Log.e("cannot get shm image"); return; } Rect rc; rc.right = buf.width; rc.bottom = buf.height; switch ( _image.bpp ) { case 32: { for (int y = rc.top; y 0; --screen_num, xcb_screen_next(&screen_iter)) { } _xcbscreen = screen_iter.data; } else { _xcbscreen = xcb_setup_roots_iterator( xcb_get_setup(_xcbconnection) ).data; } _xcbscreendepth = xcb_aux_get_depth(_xcbconnection, _xcbscreen); if (_enableOpengl) { int versionMajor; int versionMinor; if (!glXQueryVersion(_display, &versionMajor, &versionMinor)) { Log.e("Cannot get GLX version"); } else { Log.e("GLX version: ", versionMajor, ".", versionMinor); } } return true; } XCBWindow getWindow(xcb_window_t w) { if (w in _windowMap) return _windowMap[w]; return null; } override Window createWindow(string windowCaption, Window parent) { XCBWindow res = new XCBWindow(windowCaption, parent); _windowMap[res.windowId] = res; return res; } override int enterMessageLoop() { Log.i("entering message loop"); int done = 0; xcb_generic_event_t *e; xcb_key_symbols_t * keysyms;// = xcb_key_symbols_alloc(_xcbconnection); keysyms = xcb_key_symbols_alloc(_xcbconnection); if (!keysyms) Log.e("xcb_key_symbols_alloc returned null"); /* event loop */ do { e = xcb_wait_for_event(_xcbconnection); if (e is null) { Log.w("NULL event received. Exiting message loop"); break; } switch (e.response_type & ~0x80) { case XCB_CREATE_NOTIFY: { xcb_create_notify_event_t *event = cast(xcb_create_notify_event_t *)e; Log.i("XCB_CREATE_NOTIFY"); XCBWindow window = getWindow(event.window); if (window !is null) { // } else { Log.w("Received message for unknown window", event.window); } break; } case XCB_DESTROY_NOTIFY: { xcb_destroy_notify_event_t *event = cast(xcb_destroy_notify_event_t *)e; Log.i("XCB_DESTROY_NOTIFY"); XCBWindow window = getWindow(event.window); if (window !is null) { // } else { Log.w("Received message for unknown window", event.window); } break; } case XCB_MAP_NOTIFY: { xcb_map_notify_event_t *event = cast(xcb_map_notify_event_t *)e; Log.i("XCB_MAP_NOTIFY"); XCBWindow window = getWindow(event.window); if (window !is null) { // } else { Log.w("Received message for unknown window", event.window); } break; } case XCB_UNMAP_NOTIFY: { xcb_unmap_notify_event_t *event = cast(xcb_unmap_notify_event_t *)e; Log.i("XCB_UNMAP_NOTIFY"); XCBWindow window = getWindow(event.window); if (window !is null) { // } else { Log.w("Received message for unknown window", event.window); } break; } case XCB_VISIBILITY_NOTIFY: { xcb_visibility_notify_event_t *event = cast(xcb_visibility_notify_event_t *)e; Log.i("XCB_VISIBILITY_NOTIFY ", event.state); XCBWindow window = getWindow(event.window); if (window !is null) { // } else { Log.w("Received message for unknown window", event.window); } break; } case XCB_REPARENT_NOTIFY: { xcb_reparent_notify_event_t *event = cast(xcb_reparent_notify_event_t *)e; Log.i("XCB_REPARENT_NOTIFY"); break; } case XCB_CONFIGURE_NOTIFY: { xcb_configure_notify_event_t *event = cast(xcb_configure_notify_event_t *)e; Log.i("XCB_CONFIGURE_NOTIFY ", event.width, "x", event.height); XCBWindow window = getWindow(event.window); if (window !is null) { // window.onResize(event.width, event.height); } else { Log.w("Received message for unknown window", event.window); } break; } case XCB_EXPOSE: { /* draw or redraw the window */ xcb_expose_event_t *expose = cast(xcb_expose_event_t *)e; Log.i("XCB_EXPOSE"); XCBWindow window = getWindow(expose.window); if (window !is null) { window.processExpose(expose); } else { Log.w("Received message for unknown window", expose.window); } break; } case XCB_BUTTON_PRESS: { xcb_button_press_event_t *bp = cast(xcb_button_press_event_t *)e; Log.d("XCB_BUTTON_PRESS"); XCBWindow window = getWindow(bp.event); if (window !is null) { // window.processMouseEvent(MouseAction.ButtonDown, bp.detail, bp.state, bp.event_x, bp.event_y); } else { Log.w("Received message for unknown window", bp.event); } break; } case XCB_BUTTON_RELEASE: { Log.d("XCB_BUTTON_RELEASE"); xcb_button_release_event_t *br = cast(xcb_button_release_event_t *)e; XCBWindow window = getWindow(br.event); if (window !is null) { // window.processMouseEvent(MouseAction.ButtonUp, br.detail, br.state, br.event_x, br.event_y); } else { Log.w("Received message for unknown window", br.event); } break; } case XCB_MOTION_NOTIFY: { xcb_motion_notify_event_t *motion = cast(xcb_motion_notify_event_t *)e; //Log.d("XCB_MOTION_NOTIFY ", motion.event, " at coords ", motion.event_x, ", ", motion.event_y); XCBWindow window = getWindow(motion.event); if (window !is null) { // window.processMouseEvent(MouseAction.Move, 0, motion.state, motion.event_x, motion.event_y); } else { Log.w("Received message for unknown window", motion.event); } break; } case XCB_ENTER_NOTIFY: { xcb_enter_notify_event_t *enter = cast(xcb_enter_notify_event_t *)e; Log.d("XCB_ENTER_NOTIFY ", enter.event, " at coords ", enter.event_x, ", ", enter.event_y); break; } case XCB_LEAVE_NOTIFY: { xcb_leave_notify_event_t *leave = cast(xcb_leave_notify_event_t *)e; Log.d("XCB_LEAVE_NOTIFY ", leave.event, " at coords ", leave.event_x, ", ", leave.event_y); XCBWindow window = getWindow(leave.event); if (window !is null) { // window.processMouseEvent(MouseAction.Leave, 0, leave.state, leave.event_x, leave.event_y); } else { Log.w("Received message for unknown window", leave.event); } break; } case XCB_KEY_PRESS: { xcb_key_press_event_t *kp = cast(xcb_key_press_event_t *)e; //print_modifiers(kp.state); Log.d("XCB_KEY_PRESS ", kp.event, " key=", kp.detail); XCBWindow window = getWindow(kp.event); if (window !is null) { //xcb_keysym_t keysym = xcb_key_press_lookup_keysym(keysyms, kp, xcb_lookup_t.xcb_lookup_key_sym_t); xcb_keysym_t keysym = xcb_key_press_lookup_keysym(keysyms, kp, 2); Log.d("xcb_key_press_lookup_keysym - key=", kp.detail, " keysym=", keysym); //xcb_keysym_t keysym = xcb_key_symbols_get_keysym(keysyms, kp.detail, xcb_lookup_t.xcb_lookup_key_sym_t); window.processKeyEvent(KeyAction.KeyDown, keysym, kp.state); } //if (kp.detail == XK_space) // exist by space // done = 1; break; } case XCB_KEY_RELEASE: { xcb_key_release_event_t *kr = cast(xcb_key_release_event_t *)e; //print_modifiers(kr.state); Log.d("XCB_KEY_RELEASE ", kr.event, " key=", kr.detail); XCBWindow window = getWindow(kr.event); if (window !is null) { //xcb_keysym_t keysym = xcb_key_symbols_get_keysym(keysyms, kr.detail, xcb_lookup_t.xcb_lookup_key_sym_t); xcb_keysym_t keysym = xcb_key_release_lookup_keysym(keysyms, kr, 2); window.processKeyEvent(KeyAction.KeyUp, keysym, kr.state); } break; } default: Log.v("unknown event: ", e.response_type & ~0x80); break; } free(e); } while(!done); if (keysyms) xcb_key_symbols_free(keysyms); Log.i("exiting message loop"); return 0; } /// retrieves text from clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux) override dstring getClipboardText(bool mouseBuffer = false) { return ""d; } /// sets text to clipboard (when mouseBuffer == true, use mouse selection clipboard - under linux) override void setClipboardText(dstring text, bool mouseBuffer = false) { } protected XCBWindow[xcb_window_t] _windowMap; } // entry point extern(C) int UIAppMain(string[] args); int main(string[] args) { setStderrLogger(); Log.setLogLevel(LogLevel.Trace); FreeTypeFontManager ft = new FreeTypeFontManager(); ft.registerFont("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FontFamily.SansSerif, "DejaVu", false, FontWeight.Normal); FontManager.instance = ft; currentTheme = createDefaultTheme(); XCBPlatform xcb = new XCBPlatform(); if (!xcb.connect()) { return 1; } Platform.setInstance(xcb); int res = 0; static if (true) { res = UIAppMain(args); } else { Window window = xcb.createWindow("Window Caption", null); window.show(); res = xcb.enterMessageLoop(); } Platform.setInstance(null); Log.d("Destroying XCB platform"); destroy(xcb); currentTheme = null; drawableCache = null; imageCache = null; FontManager.instance = null; Log.d("Exiting main"); return res; }