diff options
Diffstat (limited to 'platform/x11/os_x11.cpp')
-rw-r--r-- | platform/x11/os_x11.cpp | 1300 |
1 files changed, 1300 insertions, 0 deletions
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp new file mode 100644 index 0000000000..79dd6f1234 --- /dev/null +++ b/platform/x11/os_x11.cpp @@ -0,0 +1,1300 @@ +/*************************************************************************/ +/* os_x11.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "servers/visual/visual_server_raster.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/gles1/rasterizer_gles1.h" +#include "os_x11.h" +#include "key_mapping_x11.h" +#include <stdio.h> +#include <stdlib.h> +#include "print_string.h" +#include "servers/physics/physics_server_sw.h" + +#include "X11/Xutil.h" +#include "main/main.h" + + + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <linux/joystick.h> + +//stupid linux.h +#ifdef KEY_TAB +#undef KEY_TAB +#endif + + +#include <X11/Xatom.h> +#include "os/pc_joystick_map.h" + +#undef CursorShape + +int OS_X11::get_video_driver_count() const { + + return 2; +} +const char * OS_X11::get_video_driver_name(int p_driver) const { + + return p_driver==0?"GLES2":"GLES1"; +} +OS::VideoMode OS_X11::get_default_video_mode() const { + + return OS::VideoMode(800,600,false); +} + +void OS_X11::initialize(const VideoMode& p_desired,int p_video_driver,int p_audio_driver) { + + last_button_state=0; + dpad_last[0]=0; + dpad_last[1]=0; + + xmbstring=NULL; + event_id=0; + x11_window=0; + last_click_ms=0; + args=OS::get_singleton()->get_cmdline_args(); + current_videomode=p_desired; + main_loop=NULL; + last_timestamp=0; + last_mouse_pos_valid=false; + last_keyrelease_time=0; + + if (get_render_thread_mode()==RENDER_SEPARATE_THREAD) { + XInitThreads(); + } + + /** XLIB INITIALIZATION **/ + x11_display = XOpenDisplay(NULL); + + char * modifiers = XSetLocaleModifiers ("@im=none"); + ERR_FAIL_COND( modifiers == NULL ); + + xim = XOpenIM (x11_display, NULL, NULL, NULL); + + + if (xim == NULL) { + WARN_PRINT("XOpenIM failed"); + xim_style=NULL; + } else { + ::XIMStyles *xim_styles=NULL; + xim_style=0; + char *imvalret=NULL; + imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL); + if (imvalret != NULL || xim_styles == NULL) { + fprintf (stderr, "Input method doesn't support any styles\n"); + } + + if (xim_styles) { + xim_style = 0; + for (int i=0;i<xim_styles->count_styles;i++) { + + if (xim_styles->supported_styles[i] == + (XIMPreeditNothing | XIMStatusNothing)) { + + xim_style = xim_styles->supported_styles[i]; + break; + } + } + + XFree (xim_styles); + } + } + + /* + char* windowid = getenv("GODOT_WINDOWID"); + if (windowid) { + + //freopen("/home/punto/stdout", "w", stdout); + //reopen("/home/punto/stderr", "w", stderr); + x11_window = atol(windowid); + + XWindowAttributes xwa; + XGetWindowAttributes(x11_display,x11_window,&xwa); + + current_videomode.width = xwa.width; + current_videomode.height = xwa.height; + }; + */ + + // maybe contextgl wants to be in charge of creating the window + //print_line("def videomode "+itos(current_videomode.width)+","+itos(current_videomode.height)); +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + context_gl = memnew( ContextGL_X11( x11_display, x11_window,current_videomode, false ) ); + context_gl->initialize(); + + if (p_video_driver == 0) { + rasterizer = memnew( RasterizerGLES2 ); + } else { + rasterizer = memnew( RasterizerGLES1 ); + }; + +#endif + visual_server = memnew( VisualServerRaster(rasterizer) ); + + if (get_render_thread_mode()!=RENDER_THREAD_UNSAFE) { + + visual_server =memnew(VisualServerWrapMT(visual_server,get_render_thread_mode()==RENDER_SEPARATE_THREAD)); + } + + AudioDriverManagerSW::get_driver(p_audio_driver)->set_singleton(); + + if (AudioDriverManagerSW::get_driver(p_audio_driver)->init()!=OK) { + + ERR_PRINT("Initializing audio failed."); + } + + sample_manager = memnew( SampleManagerMallocSW ); + audio_server = memnew( AudioServerSW(sample_manager) ); + audio_server->init(); + spatial_sound_server = memnew( SpatialSoundServerSW ); + spatial_sound_server->init(); + spatial_sound_2d_server = memnew( SpatialSound2DServerSW ); + spatial_sound_2d_server->init(); + + + ERR_FAIL_COND(!visual_server); + ERR_FAIL_COND(x11_window==0); + + XSetWindowAttributes new_attr; + + new_attr.event_mask=KeyPressMask | KeyReleaseMask | ButtonPressMask | + ButtonReleaseMask | EnterWindowMask | + LeaveWindowMask | PointerMotionMask | + Button1MotionMask | + Button2MotionMask | Button3MotionMask | + Button4MotionMask | Button5MotionMask | + ButtonMotionMask | KeymapStateMask | + ExposureMask | VisibilityChangeMask | + StructureNotifyMask | + SubstructureNotifyMask | SubstructureRedirectMask | + FocusChangeMask | PropertyChangeMask | + ColormapChangeMask | OwnerGrabButtonMask; + + XChangeWindowAttributes(x11_display, x11_window,CWEventMask,&new_attr); + + wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true); + XSetWMProtocols(x11_display, x11_window, &wm_delete, 1); + + + if (xim && xim_style) { + + xic = XCreateIC (xim,XNInputStyle, xim_style,XNClientWindow,x11_window,XNFocusWindow, x11_window, (char*)NULL); + } else { + + xic=NULL; + WARN_PRINT("XCreateIC couldn't create xic"); + + } + + XcursorSetTheme(x11_display,"default"); + cursor_size = XcursorGetDefaultSize(x11_display); + cursor_theme = XcursorGetTheme(x11_display); + + if (!cursor_theme) { + print_line("not found theme"); + cursor_theme="default"; + } + + for(int i=0;i<CURSOR_MAX;i++) { + + cursors[i]=None; + } + + current_cursor=CURSOR_ARROW; + + if (cursor_theme) { + //print_line("cursor theme: "+String(cursor_theme)); + for(int i=0;i<CURSOR_MAX;i++) { + + static const char *cursor_file[]={ + "left_ptr", + "xterm", + "hand2", + "cross", + "watch", + "left_ptr_watch", + "fleur", + "hand1", + "X_cursor", + "sb_v_double_arrow", + "sb_h_double_arrow", + "size_bdiag", + "size_fdiag", + "hand1", + "sb_v_double_arrow", + "sb_h_double_arrow", + "question_arrow" + }; + + XcursorImage *img = XcursorLibraryLoadImage(cursor_file[i],cursor_theme,cursor_size); + if (img) { + cursors[i]=XcursorImageLoadCursor(x11_display,img); + //print_line("found cursor: "+String(cursor_file[i])+" id "+itos(cursors[i])); + } else { + if (OS::is_stdout_verbose()) + print_line("failed cursor: "+String(cursor_file[i])); + } + } + + } + + + { + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor col; + Cursor cursor; + + cursormask = XCreatePixmap(x11_display, RootWindow(x11_display,DefaultScreen(x11_display)), 1, 1, 1); + xgc.function = GXclear; + gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc); + XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1); + col.pixel = 0; + col.red = 0; + col.flags = 4; + cursor = XCreatePixmapCursor(x11_display, + cursormask, cursormask, + &col, &col, 0, 0); + XFreePixmap(x11_display, cursormask); + XFreeGC(x11_display, gc); + + + + if (cursor == None) + { + ERR_PRINT("FAILED CREATING CURSOR"); + } + + null_cursor=cursor; + } + set_cursor_shape(CURSOR_BUSY); + + + visual_server->init(); + // + physics_server = memnew( PhysicsServerSW ); + physics_server->init(); + physics_2d_server = memnew( Physics2DServerSW ); + physics_2d_server->init(); + + input = memnew( InputDefault ); + + probe_joystick(); + + _ensure_data_dir(); + + net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False); + + + //printf("got map notify\n"); + +} +void OS_X11::finalize() { + + if(main_loop) + memdelete(main_loop); + main_loop=NULL; + + spatial_sound_server->finish(); + memdelete(spatial_sound_server); + spatial_sound_2d_server->finish(); + memdelete(spatial_sound_2d_server); + + //if (debugger_connection_console) { +// memdelete(debugger_connection_console); +//} + + audio_server->finish(); + memdelete(audio_server); + memdelete(sample_manager); + + visual_server->finish(); + memdelete(visual_server); + memdelete(rasterizer); + + physics_server->finish(); + memdelete(physics_server); + + physics_2d_server->finish(); + memdelete(physics_2d_server); + + memdelete(input); + +#if defined(OPENGL_ENABLED) || defined(LEGACYGL_ENABLED) + memdelete(context_gl); +#endif + + + XCloseDisplay(x11_display); + if (xmbstring) + memfree(xmbstring); + + args.clear(); +} + + +void OS_X11::set_mouse_mode(MouseMode p_mode) { + + print_line("WUTF "+itos(p_mode)+" old "+itos(mouse_mode)); + if (p_mode==mouse_mode) + return; + + if (mouse_mode==MOUSE_MODE_CAPTURED) + XUngrabPointer(x11_display, CurrentTime); + if (mouse_mode!=MOUSE_MODE_VISIBLE && p_mode==MOUSE_MODE_VISIBLE) + XUndefineCursor(x11_display,x11_window); + if (p_mode!=MOUSE_MODE_VISIBLE && mouse_mode==MOUSE_MODE_VISIBLE) { + XDefineCursor(x11_display,x11_window,null_cursor); + } + + mouse_mode=p_mode; + + if (mouse_mode==MOUSE_MODE_CAPTURED) { + if (XGrabPointer(x11_display, x11_window, True, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, GrabModeAsync, + x11_window, None, CurrentTime) != + GrabSuccess) { + ERR_PRINT("NO GRAB"); + } + + center.x = current_videomode.width/2; + center.y = current_videomode.height/2; + XWarpPointer(x11_display, None, x11_window, + 0,0,0,0, (int)center.x, (int)center.y); + } + +} + +OS::MouseMode OS_X11::get_mouse_mode() const { + + return mouse_mode; +} + + + +int OS_X11::get_mouse_button_state() const { + + return last_button_state; +} + +Point2 OS_X11::get_mouse_pos() const { + + return last_mouse_pos; +} + +void OS_X11::set_window_title(const String& p_title) { + + XStoreName(x11_display,x11_window,p_title.utf8().get_data()); +} + +void OS_X11::set_video_mode(const VideoMode& p_video_mode,int p_screen) { + + +} +OS::VideoMode OS_X11::get_video_mode(int p_screen) const { + + return current_videomode; +} +void OS_X11::get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen) const { + + +} + + +InputModifierState OS_X11::get_key_modifier_state(unsigned int p_x11_state) { + + InputModifierState state; + + state.shift = (p_x11_state&ShiftMask); + state.control = (p_x11_state&ControlMask); + state.alt = (p_x11_state&Mod1Mask /*|| p_x11_state&Mod5Mask*/); //altgr should not count as alt + state.meta = (p_x11_state&Mod4Mask); + + return state; +} + +unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_state) { + + unsigned int state=0; + + if (p_x11_state&Button1Mask) { + + state|=1<<0; + } + + if (p_x11_state&Button3Mask) { + + state|=1<<1; + } + + if (p_x11_state&Button2Mask) { + + state|=1<<2; + } + + if (p_x11_state&Button4Mask) { + + state|=1<<3; + } + + if (p_x11_state&Button5Mask) { + + state|=1<<4; + } + + last_button_state=state; + return state; +} + +void OS_X11::handle_key_event(XKeyEvent *p_event) { + + + // X11 functions don't know what const is + XKeyEvent *xkeyevent = p_event; + + // This code was pretty difficult to write. + // The docs stink and every toolkit seems to + // do it in a different way. + + /* Phase 1, obtain a proper keysym */ + + // This was also very difficult to figure out. + // You'd expect you could just use Keysym provided by + // XKeycodeToKeysym to obtain internationalized + // input.. WRONG!! + // you must use XLookupString (???) which not only wastes + // cycles generating an unnecesary string, but also + // still works in half the cases. (won't handle deadkeys) + // For more complex input methods (deadkeys and more advanced) + // you have to use XmbLookupString (??). + // So.. then you have to chosse which of both results + // you want to keep. + // This is a real bizarreness and cpu waster. + + KeySym keysym_keycode=0; // keysym used to find a keycode + KeySym keysym_unicode=0; // keysym used to find unicode + + int nbytes=0; // bytes the string takes + + // XLookupString returns keysyms usable as nice scancodes/ + char str[256+1]; + nbytes=XLookupString(xkeyevent, str, 256, &keysym_keycode, NULL); + + // Meanwhile, XLookupString returns keysyms useful for unicode. + + + if (!xmbstring) { + // keep a temporary buffer for the string + xmbstring=(char*)memalloc(sizeof(char)*8); + xmblen=8; + } + + if (xkeyevent->type == KeyPress && xic) { + + Status status; + do { + + int mnbytes = XmbLookupString (xic, xkeyevent, xmbstring, xmblen - 1, &keysym_unicode, &status); + xmbstring[mnbytes] = '\0'; + + if (status == XBufferOverflow) { + xmblen = mnbytes + 1; + xmbstring = (char*)memrealloc (xmbstring, xmblen); + } + } while (status == XBufferOverflow); + } + + + /* Phase 2, obtain a pigui keycode from the keysym */ + + // KeyMappingX11 just translated the X11 keysym to a PIGUI + // keysym, so it works in all platforms the same. + + unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode); + + /* Phase 3, obtain an unicode character from the keysym */ + + // KeyMappingX11 also translates keysym to unicode. + // It does a binary search on a table to translate + // most properly. + //print_line("keysym_unicode: "+rtos(keysym_unicode)); + unsigned int unicode = keysym_unicode>0? KeyMappingX11::get_unicode_from_keysym(keysym_unicode):0; + + + /* Phase 4, determine if event must be filtered */ + + // This seems to be a side-effect of using XIM. + // XEventFilter looks like a core X11 funciton, + // but it's actually just used to see if we must + // ignore a deadkey, or events XIM determines + // must not reach the actual gui. + // Guess it was a design problem of the extension + + bool keypress = xkeyevent->type == KeyPress; + + if (xkeyevent->type == KeyPress && xic) { + if (XFilterEvent((XEvent*)xkeyevent, x11_window)) + return; + } + + if (keycode==0 && unicode==0) + return; + + /* Phase 5, determine modifier mask */ + + // No problems here, except I had no way to + // know Mod1 was ALT and Mod4 was META (applekey/winkey) + // just tried Mods until i found them. + + //print_line("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask)); + + InputModifierState state = get_key_modifier_state(xkeyevent->state); + + /* Phase 6, determine echo character */ + + // Echo characters in X11 are a keyrelease and a keypress + // one after the other with the (almot) same timestamp. + // To detect them, i use XPeekEvent and check that their + // difference in time is below a treshold. + + bool echo=false; + + if (xkeyevent->type == KeyPress) { + + // saved the time of the last keyrelease to see + // if it's the same as this keypress. + if (xkeyevent->time==last_keyrelease_time) + echo=true; + + } else { + + // make sure there are events pending, + // so this call won't block. + if (XPending(x11_display)>0) { + XEvent peek_event; + XPeekEvent(x11_display, &peek_event); + + // I'm using a treshold of 5 msecs, + // since sometimes there seems to be a little + // jitter. I'm still not convinced that all this approach + // is correct, but the xorg developers are + // not very helpful today. + + ::Time tresh=ABS(peek_event.xkey.time-xkeyevent->time); + if (peek_event.type == KeyPress && tresh<5 ) + echo=true; + + // use the time from peek_event so it always works + last_keyrelease_time=peek_event.xkey.time; + } else { + last_keyrelease_time=xkeyevent->time; + } + + // save the time to check for echo when keypress happens + + } + + + /* Phase 7, send event to Window */ + + InputEvent event; + event.ID=++event_id; + event.type = InputEvent::KEY; + event.device=0; + event.key.mod=state; + event.key.pressed=keypress; + + if (keycode>='a' && keycode<='z') + keycode-='a'-'A'; + + event.key.scancode=keycode; + event.key.unicode=unicode; + event.key.echo=echo; + + if (event.key.scancode==KEY_BACKTAB) { + //make it consistent accross platforms. + event.key.scancode=KEY_TAB; + event.key.mod.shift=true; + } + + //printf("key: %x\n",event.key.scancode); + input->parse_input_event( event); + + +} + +void OS_X11::process_xevents() { + + //printf("checking events %i\n", XPending(x11_display)); + + bool do_mouse_warp=false; + + while (XPending(x11_display) > 0) { + XEvent event; + XNextEvent(x11_display, &event); + + switch (event.type) { + case Expose: + Main::force_redraw(); + break; + + case NoExpose: + minimized = true; + break; + + case VisibilityNotify: { + + XVisibilityEvent * visibility = (XVisibilityEvent *)&event; + minimized = (visibility->state == VisibilityFullyObscured); + + } break; + + case FocusIn: + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + if (mouse_mode==MOUSE_MODE_CAPTURED) { + XGrabPointer(x11_display, x11_window, True, + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask, GrabModeAsync, GrabModeAsync, + x11_window, None, CurrentTime); + } + break; + + case FocusOut: + main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + if (mouse_mode==MOUSE_MODE_CAPTURED) { + //dear X11, I try, I really try, but you never work, you do whathever you want. + XUngrabPointer(x11_display, CurrentTime); + } + break; + + case ConfigureNotify: + /* call resizeGLScene only if our window-size changed */ + + if ((event.xconfigure.width == current_videomode.width) && + (event.xconfigure.height == current_videomode.height)) + break; + + current_videomode.width=event.xconfigure.width; + current_videomode.height=event.xconfigure.height; + break; + case ButtonPress: + case ButtonRelease: { + + /* exit in case of a mouse button press */ + last_timestamp=event.xbutton.time; + if (mouse_mode==MOUSE_MODE_CAPTURED) { + event.xbutton.x=last_mouse_pos.x; + event.xbutton.y=last_mouse_pos.y; + } + + InputEvent mouse_event; + mouse_event.ID=++event_id; + mouse_event.type = InputEvent::MOUSE_BUTTON; + mouse_event.device=0; + mouse_event.mouse_button.mod = get_key_modifier_state(event.xbutton.state); + mouse_event.mouse_button.button_mask = get_mouse_button_state(event.xbutton.state); + mouse_event.mouse_button.x=event.xbutton.x; + mouse_event.mouse_button.y=event.xbutton.y; + mouse_event.mouse_button.global_x=event.xbutton.x; + mouse_event.mouse_button.global_y=event.xbutton.y; + mouse_event.mouse_button.button_index=event.xbutton.button; + if (mouse_event.mouse_button.button_index==2) + mouse_event.mouse_button.button_index=3; + else if (mouse_event.mouse_button.button_index==3) + mouse_event.mouse_button.button_index=2; + + mouse_event.mouse_button.pressed=(event.type==ButtonPress); + + + if (event.type==ButtonPress && event.xbutton.button==1) { + + uint64_t diff = get_ticks_usec()/1000 - last_click_ms; + + if (diff<400 && Point2(last_click_pos).distance_to(Point2(event.xbutton.x,event.xbutton.y))<5) { + + last_click_ms=0; + last_click_pos = Point2(-100,-100); + mouse_event.mouse_button.doubleclick=true; + mouse_event.ID=++event_id; + + } else { + last_click_ms+=diff; + last_click_pos = Point2(event.xbutton.x,event.xbutton.y); + } + } + + input->parse_input_event( mouse_event); + + + } break; + case MotionNotify: { + + + last_timestamp=event.xmotion.time; + + // Motion is also simple. + // A little hack is in order + // to be able to send relative motion events. + + Point2i pos( event.xmotion.x, event.xmotion.y ); + + if (mouse_mode==MOUSE_MODE_CAPTURED) { +#if 1 + Vector2 c = Point2i(current_videomode.width/2,current_videomode.height/2); + if (pos==Point2i(current_videomode.width/2,current_videomode.height/2)) { + //this sucks, it's a hack, etc and is a little inaccurate, etc. + //but nothing I can do, X11 sucks. + + center=pos; + break; + } + + Point2i ncenter = pos; + pos = last_mouse_pos + ( pos-center ); + center=ncenter; + do_mouse_warp=true; +#else + //Dear X11, thanks for making my life miserable + + center.x = current_videomode.width/2; + center.y = current_videomode.height/2; + pos = last_mouse_pos + ( pos-center ); + if (pos==last_mouse_pos) + break; + XWarpPointer(x11_display, None, x11_window, + 0,0,0,0, (int)center.x, (int)center.y); +#endif + + } + + + if (!last_mouse_pos_valid) { + + last_mouse_pos=pos; + last_mouse_pos_valid=true; + } + + Point2i rel = pos - last_mouse_pos; + + InputEvent motion_event; + motion_event.ID=++event_id; + motion_event.type=InputEvent::MOUSE_MOTION; + motion_event.device=0; + + motion_event.mouse_motion.mod = get_key_modifier_state(event.xmotion.state); + motion_event.mouse_motion.button_mask = get_mouse_button_state(event.xmotion.state); + motion_event.mouse_motion.x=pos.x; + motion_event.mouse_motion.y=pos.y; + input->set_mouse_pos(pos); + motion_event.mouse_motion.global_x=pos.x; + motion_event.mouse_motion.global_y=pos.y; + motion_event.mouse_motion.speed_x=input->get_mouse_speed().x; + motion_event.mouse_motion.speed_y=input->get_mouse_speed().y; + + motion_event.mouse_motion.relative_x=rel.x; + motion_event.mouse_motion.relative_y=rel.y; + + last_mouse_pos=pos; + + input->parse_input_event( motion_event); + + } break; + case KeyPress: + case KeyRelease: { + + last_timestamp=event.xkey.time; + + // key event is a little complex, so + // it will be handled in it's own function. + handle_key_event( (XKeyEvent*)&event ); + } break; + case SelectionRequest: { + + XSelectionRequestEvent *req; + XEvent e, respond; + e = event; + + req=&(e.xselectionrequest); + if (req->target == XA_STRING || req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || + req->target == XInternAtom(x11_display, "UTF8_STRING", 0)) + { + CharString clip = OS::get_clipboard().utf8(); + XChangeProperty (x11_display, + req->requestor, + req->property, + req->target, + 8, + PropModeReplace, + (unsigned char*)clip.get_data(), + clip.length()); + respond.xselection.property=req->property; + } else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) { + + Atom data[2]; + data[0] = XInternAtom(x11_display, "UTF8_STRING", 0); + data[1] = XA_STRING; + XChangeProperty (x11_display, req->requestor, req->property, req->target, + 8, PropModeReplace, (unsigned char *) &data, + sizeof (data)); + respond.xselection.property=req->property; + + } else { + printf ("No String %x\n", + (int)req->target); + respond.xselection.property= None; + } + respond.xselection.type= SelectionNotify; + respond.xselection.display= req->display; + respond.xselection.requestor= req->requestor; + respond.xselection.selection=req->selection; + respond.xselection.target= req->target; + respond.xselection.time = req->time; + XSendEvent (x11_display, req->requestor,0,0,&respond); + XFlush (x11_display); + } break; + + + case ClientMessage: + + if ((unsigned int)event.xclient.data.l[0]==(unsigned int)wm_delete) + main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + break; + default: + break; + } + } + + XFlush(x11_display); + + if (do_mouse_warp) { + + XWarpPointer(x11_display, None, x11_window, + 0,0,0,0, (int)current_videomode.width/2, (int)current_videomode.height/2); + + } +} + +MainLoop *OS_X11::get_main_loop() const { + + return main_loop; +} + +void OS_X11::delete_main_loop() { + + if (main_loop) + memdelete(main_loop); + main_loop=NULL; +} + +void OS_X11::set_main_loop( MainLoop * p_main_loop ) { + + main_loop=p_main_loop; + input->set_main_loop(p_main_loop); +} + +bool OS_X11::can_draw() const { + + return !minimized; +}; + +void OS_X11::set_clipboard(const String& p_text) { + + OS::set_clipboard(p_text); + + XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime); + XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, CurrentTime); +}; + +static String _get_clipboard(Atom p_source, Window x11_window, ::Display* x11_display, String p_internal_clipboard) { + + String ret; + + Atom type; + Atom selection = XA_PRIMARY; + int format, result; + unsigned long len, bytes_left, dummy; + unsigned char *data; + Window Sown = XGetSelectionOwner (x11_display, p_source); + + if (Sown == x11_window) { + + printf("returning internal clipboard\n"); + return p_internal_clipboard; + }; + + if (Sown != None) { + XConvertSelection (x11_display, p_source, XA_STRING, selection, + x11_window, CurrentTime); + XFlush (x11_display); + while (true) { + XEvent event; + XNextEvent(x11_display, &event); + if (event.type == SelectionNotify && event.xselection.requestor == x11_window) { + break; + }; + }; + + // + // Do not get any data, see how much data is there + // + XGetWindowProperty (x11_display, x11_window, + selection, // Tricky.. + 0, 0, // offset - len + 0, // Delete 0==FALSE + AnyPropertyType, //flag + &type, // return type + &format, // return format + &len, &bytes_left, //that + &data); + // DATA is There + if (bytes_left > 0) + { + result = XGetWindowProperty (x11_display, x11_window, + selection, 0,bytes_left,0, + AnyPropertyType, &type,&format, + &len, &dummy, &data); + if (result == Success) { + ret.parse_utf8((const char*)data); + } else printf ("FAIL\n"); + XFree (data); + } + } + + return ret; + +}; + +String OS_X11::get_clipboard() const { + + String ret; + ret = _get_clipboard(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, x11_display, OS::get_clipboard()); + + if (ret == "") { + ret = _get_clipboard(XA_PRIMARY, x11_window, x11_display, OS::get_clipboard()); + }; + + return ret; +}; + +String OS_X11::get_name() { + + return "X11"; +} + + +void OS_X11::close_joystick(int p_id) { + + if (p_id == -1) { + for (int i=0; i<JOYSTICKS_MAX; i++) { + + close_joystick(i); + }; + return; + }; + + + if (joysticks[p_id].fd != -1) { + close(joysticks[p_id].fd); + joysticks[p_id].fd = -1; + }; +}; + +void OS_X11::probe_joystick(int p_id) { + + if (p_id == -1) { + + for (int i=0; i<JOYSTICKS_MAX; i++) { + + probe_joystick(i); + }; + return; + }; + + close_joystick(p_id); + + const char *joy_names[] = { + "/dev/input/js%d", + "/dev/js%d", + NULL + }; + + int i=0; + while(joy_names[i]) { + + char fname[64]; + sprintf(fname, joy_names[i], p_id); + int fd = open(fname, O_RDONLY); + if (fd != -1) { + + fcntl( fd, F_SETFL, O_NONBLOCK ); + joysticks[p_id] = Joystick(); // this will reset the axis array + joysticks[p_id].fd = fd; + break; // don't try the next name + }; + + ++i; + }; +}; + +void OS_X11::move_window_to_foreground() { + + XRaiseWindow(x11_display,x11_window); +} + +void OS_X11::process_joysticks() { + + int bytes; + js_event events[32]; + InputEvent ievent; + for (int i=0; i<JOYSTICKS_MAX; i++) { + + if (joysticks[i].fd == -1) + continue; + ievent.device = i; + + while ( (bytes = read(joysticks[i].fd, &events, sizeof(events))) > 0) { + + int ev_count = bytes / sizeof(js_event); + for (int j=0; j<ev_count; j++) { + + js_event& event = events[j]; + + //printf("got event on joystick %i, %i, %i, %i, %i\n", i, joysticks[i].fd, event.type, event.number, event.value); + if (event.type & JS_EVENT_INIT) + continue; + + switch (event.type & ~JS_EVENT_INIT) { + + case JS_EVENT_AXIS: + + if (joysticks[i].last_axis[event.number] != event.value) { + + if (event.number==5 || event.number==6) { + + int axis=event.number-5; + int val = event.value; + if (val<0) + val=-1; + if (val>0) + val=+1; + + InputEvent ev; + ev.type = InputEvent::JOYSTICK_BUTTON; + ev.ID = ++event_id; + + + if (val!=dpad_last[axis]) { + + int prev_val = dpad_last[axis]; + if (prev_val!=0) { + + ev.joy_button.pressed=false; + ev.joy_button.pressure=0.0; + if (event.number==5) + ev.joy_button.button_index=JOY_DPAD_LEFT+(prev_val+1)/2; + if (event.number==6) + ev.joy_button.button_index=JOY_DPAD_UP+(prev_val+1)/2; + + input->parse_input_event( ev ); + } + } + + if (val!=0) { + + ev.joy_button.pressed=true; + ev.joy_button.pressure=1.0; + if (event.number==5) + ev.joy_button.button_index=JOY_DPAD_LEFT+(val+1)/2; + if (event.number==6) + ev.joy_button.button_index=JOY_DPAD_UP+(val+1)/2; + + input->parse_input_event( ev ); + } + + + dpad_last[axis]=val; + + } + //print_line("ev: "+itos(event.number)+" val: "+ rtos((float)event.value / (float)MAX_JOY_AXIS)); + if (event.number >= JOY_AXIS_MAX) + break; + //ERR_FAIL_COND(event.number >= JOY_AXIS_MAX); + ievent.type = InputEvent::JOYSTICK_MOTION; + ievent.ID = ++event_id; + ievent.joy_motion.axis = _pc_joystick_get_native_axis(event.number); + ievent.joy_motion.axis_value = (float)event.value / (float)MAX_JOY_AXIS; + joysticks[i].last_axis[event.number] = event.value; + input->parse_input_event( ievent ); + }; + break; + + case JS_EVENT_BUTTON: + + + ievent.type = InputEvent::JOYSTICK_BUTTON; + ievent.ID = ++event_id; + ievent.joy_button.button_index = _pc_joystick_get_native_button(event.number); + ievent.joy_button.pressed = event.value; + input->parse_input_event( ievent ); + break; + }; + }; + }; + }; +}; + +void OS_X11::set_cursor_shape(CursorShape p_shape) { + + ERR_FAIL_INDEX(p_shape,CURSOR_MAX); + + if (p_shape==current_cursor) + return; + if (mouse_mode==MOUSE_MODE_VISIBLE) { + if (cursors[p_shape]!=None) + XDefineCursor(x11_display,x11_window,cursors[p_shape]); + else if (cursors[CURSOR_ARROW]!=None) + XDefineCursor(x11_display,x11_window,cursors[CURSOR_ARROW]); + } + + + current_cursor=p_shape; +} + + +void OS_X11::release_rendering_thread() { + + context_gl->release_current(); + +} + +void OS_X11::make_rendering_thread() { + + context_gl->make_current(); +} + +void OS_X11::swap_buffers() { + + context_gl->swap_buffers(); +} + + +void OS_X11::set_icon(const Image& p_icon) { + + //does not work, if anyone knows why, please fix + if (!p_icon.empty()) { + + Image img=p_icon; + img.convert(Image::FORMAT_RGBA); + + + int w = img.get_width(); + int h = img.get_height(); + + Vector<long> pd; + + pd.resize((2+w*h)*sizeof(long)); + + print_line("***** SET ICON ***** "+itos(w)+" "+itos(h)); + + pd[0]=w; + pd[1]=h; + + DVector<uint8_t>::Read r = img.get_data().read(); + + uint32_t *wr=(uint32_t*)&pd[2]; + + for(int i=0;i<w*h;i++) { + + uint32_t v=0; + v|=r[i*4+3]; + v<<=8; + v|=r[i*4+0]; + v<<=8; + v|=r[i*4+1]; + v<<=8; + v|=r[i*4+2]; + wr[i]=v; + } + + XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) pd.ptr(), + (2+w*h)); + } else { + XDeleteProperty(x11_display, x11_window, net_wm_icon); + } + XFlush(x11_display); + +} + + +void OS_X11::run() { + + force_quit = false; + + if (!main_loop) + return; + + main_loop->init(); + +// uint64_t last_ticks=get_ticks_usec(); + +// int frames=0; +// uint64_t frame=0; + + while (!force_quit) { + + process_xevents(); // get rid of pending events + process_joysticks(); + if (Main::iteration()==true) + break; + }; + + main_loop->finish(); +} + +OS_X11::OS_X11() { + +#ifdef RTAUDIO_ENABLED + AudioDriverManagerSW::add_driver(&driver_rtaudio); +#endif + +#ifdef ALSA_ENABLED + AudioDriverManagerSW::add_driver(&driver_alsa); +#endif + + minimized = false; + xim_style=NULL; + mouse_mode=MOUSE_MODE_VISIBLE; + + +}; |