static gboolean message_translate (ClutterBackend *backend, ClutterEvent *event, const MSG *msg, gboolean *call_def_window_proc) { ClutterBackendWin32 *backend_win32; ClutterStageWin32 *stage_win32; ClutterStage *stage; ClutterStageWindow *impl; gboolean res; backend_win32 = CLUTTER_BACKEND_WIN32 (backend); /* Do further processing only on events for the stage window */ stage = clutter_win32_get_stage_from_window (msg->hwnd); if (stage == NULL) return FALSE; impl = _clutter_stage_get_window (stage); stage_win32 = CLUTTER_STAGE_WIN32 (impl); event->any.stage = stage; res = TRUE; switch (msg->message) { case WM_SIZE: if (!stage_win32->is_foreign_win /* Ignore size changes resulting from the stage being minimized - otherwise the window size will be set to 0,0 */ && msg->wParam != SIZE_MINIMIZED) { WORD new_width = LOWORD (msg->lParam); WORD new_height = HIWORD (msg->lParam); guint old_width, old_height; clutter_actor_get_size (CLUTTER_ACTOR (stage), &old_width, &old_height); if (new_width != old_width || new_height != old_height) clutter_actor_set_size (CLUTTER_ACTOR (stage), new_width, new_height); } res = FALSE; break; case WM_SHOWWINDOW: if (msg->wParam) clutter_stage_win32_map (stage_win32); else clutter_stage_win32_unmap (stage_win32); res = FALSE; break; case WM_ACTIVATE: if (msg->wParam == WA_INACTIVE) { if (stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED) { stage_win32->state &= ~CLUTTER_STAGE_STATE_ACTIVATED; event->type = CLUTTER_STAGE_STATE; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_win32->state; } else res = FALSE; break; } else { if (!(stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED)) { stage_win32->state |= CLUTTER_STAGE_STATE_ACTIVATED; event->type = CLUTTER_STAGE_STATE; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_win32->state; } else res = FALSE; } break; case WM_PAINT: CLUTTER_NOTE (MULTISTAGE, "expose for stage:%p, redrawing", stage); clutter_redraw (stage); res = FALSE; break; case WM_DESTROY: CLUTTER_NOTE (EVENT, "WM_DESTROY"); event->type = CLUTTER_DESTROY_NOTIFY; break; case WM_CLOSE: CLUTTER_NOTE (EVENT, "WM_CLOSE"); event->type = CLUTTER_DELETE; /* The default window proc will destroy the window so we want to prevent this to allow applications to optionally destroy the window themselves */ if (call_def_window_proc) *call_def_window_proc = FALSE; break; case WM_LBUTTONDOWN: make_button_event (msg, event, 1, 1, FALSE); break; case WM_MBUTTONDOWN: make_button_event (msg, event, 2, 1, FALSE); break; case WM_RBUTTONDOWN: make_button_event (msg, event, 3, 1, FALSE); break; case WM_LBUTTONUP: make_button_event (msg, event, 1, 1, TRUE); break; case WM_MBUTTONUP: make_button_event (msg, event, 2, 1, TRUE); break; case WM_RBUTTONUP: make_button_event (msg, event, 3, 1, TRUE); break; case WM_LBUTTONDBLCLK: make_button_event (msg, event, 1, 2, FALSE); break; case WM_MBUTTONDBLCLK: make_button_event (msg, event, 2, 2, FALSE); break; case WM_RBUTTONDBLCLK: make_button_event (msg, event, 3, 2, FALSE); break; case WM_MOUSEWHEEL: stage_win32->scroll_pos += (SHORT) HIWORD (msg->wParam); event->type = CLUTTER_SCROLL; event->scroll.time = msg->time; event->scroll.modifier_state = get_modifier_state (LOWORD (msg->wParam)); /* conversion to window coordinates is required */ { POINT pt = { GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam) }; ScreenToClient (msg->hwnd, &pt); event->scroll.x = pt.x; event->scroll.y = pt.y; } if (stage_win32->scroll_pos >= WHEEL_DELTA) { event->scroll.direction = CLUTTER_SCROLL_UP; stage_win32->scroll_pos -= WHEEL_DELTA; } else if (stage_win32->scroll_pos <= -WHEEL_DELTA) { event->scroll.direction = CLUTTER_SCROLL_DOWN; stage_win32->scroll_pos += WHEEL_DELTA; } else res = FALSE; break; case WM_MOUSEMOVE: event->type = CLUTTER_MOTION; event->motion.time = msg->time; event->motion.x = GET_X_LPARAM (msg->lParam); event->motion.y = GET_Y_LPARAM (msg->lParam); event->motion.modifier_state = get_modifier_state (msg->wParam); break; case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP: { int scan_code = (msg->lParam >> 16) & 0xff; int min = 0, max = CLUTTER_WIN32_KEY_MAP_SIZE, mid; BYTE key_states[256]; /* Get the keyboard modifier states. GetKeyboardState conveniently gets the key state that was current when the last keyboard message read was generated */ GetKeyboardState(key_states); /* Binary chop to check if we have a direct mapping for this key code */ while (min < max) { mid = (min + max) / 2; if (clutter_win32_key_map[mid].win_sym == msg->wParam) { event->key.keyval = clutter_win32_key_map[mid].clutter_sym; event->key.unicode_value = 0; break; } else if (clutter_win32_key_map[mid].win_sym < msg->wParam) min = mid + 1; else max = mid; } /* If we don't have a direct mapping then try getting the unicode value of the key sym */ if (min >= max) { WCHAR ch; BYTE shift_state[256]; /* Translate to a Unicode value, but only take into account the shift key. That way Ctrl+Shift+C will generate a capital C virtual key code with a zero unicode value for example */ memset (shift_state, 0, 256); shift_state[VK_SHIFT] = key_states[VK_SHIFT]; shift_state[VK_LSHIFT] = key_states[VK_LSHIFT]; shift_state[VK_RSHIFT] = key_states[VK_RSHIFT]; shift_state[VK_CAPITAL] = key_states[VK_CAPITAL]; if (ToUnicode (msg->wParam, scan_code, shift_state, &ch, 1, 0) == 1 /* The codes in this range directly match the Latin 1 codes so we can just use the Unicode value as the key sym */ && ch >= 0x20 && ch <= 0xff) event->key.keyval = ch; else /* Otherwise we don't know what the key means but the application might be able to do something with the scan code so we might as well still generate the event */ event->key.keyval = CLUTTER_VoidSymbol; /* Get the unicode value of the keypress again using the full modifier state */ if (ToUnicode (msg->wParam, scan_code, key_states, &ch, 1, 0) == 1) event->key.unicode_value = ch; else event->key.unicode_value = 0; } event->key.type = msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE; event->key.time = msg->time; event->key.modifier_state = get_key_modifier_state (key_states); event->key.hardware_keycode = scan_code; } break; case WM_GETMINMAXINFO: { MINMAXINFO *min_max_info = (MINMAXINFO *) msg->lParam; _clutter_stage_win32_get_min_max_info (stage_win32, min_max_info); if (call_def_window_proc) *call_def_window_proc = FALSE; } break; default: /* ignore every other message */ res = FALSE; break; } return res; }
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); } }
void OS_X11::process_xevents() { //printf("checking events %i\n", XPending(x11_display)); 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 LeaveNotify: { if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED) main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT); if (input) input->set_mouse_in_window(false); } break; case EnterNotify: { if (main_loop && mouse_mode!=MOUSE_MODE_CAPTURED) main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); if (input) input->set_mouse_in_window(true); } break; case FocusIn: minimized = false; #ifdef NEW_WM_API if(current_videomode.fullscreen) { set_wm_fullscreen(true); } #endif 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: #ifdef NEW_WM_API if(current_videomode.fullscreen) { set_wm_fullscreen(false); set_window_minimized(true); } #endif 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: { // F**K YOU X11 API YOU SERIOUSLY GROSS ME OUT // YOU ARE AS GROSS AS LOOKING AT A PUTRID PILE // OF POOP STICKING OUT OF A CLOGGED TOILET // HOW THE F**K I AM SUPPOSED TO KNOW WHICH ONE // OF THE MOTION NOTIFY EVENTS IS THE ONE GENERATED // BY WARPING THE MOUSE POINTER? // YOU ARE FORCING ME TO FILTER ONE BY ONE TO FIND IT // PLEASE DO ME A FAVOR AND DIE DROWNED IN A FECAL // MOUNTAIN BECAUSE THAT'S WHERE YOU BELONG. while(true) { if (mouse_mode==MOUSE_MODE_CAPTURED && event.xmotion.x==current_videomode.width/2 && event.xmotion.y==current_videomode.height/2) { //this is likely the warp event since it was warped here center=Vector2(event.xmotion.x,event.xmotion.y); break; } if (XPending(x11_display) > 0) { XEvent tevent; XPeekEvent(x11_display, &tevent); if (tevent.type==MotionNotify) { XNextEvent(x11_display,&event); } else { break; } } else { break; } } 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 new_center = pos; pos = last_mouse_pos + ( pos - center ); center=new_center; 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; #ifdef NEW_WM_API if (mouse_mode==MOUSE_MODE_CAPTURED) { pos.x = current_videomode.width / 2; pos.y = current_videomode.height / 2; } #endif 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; // printf("rel: %d,%d\n", rel.x, rel.y ); 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); /* Window root, child; int root_x, root_y; int win_x, win_y; unsigned int mask; XQueryPointer( x11_display, x11_window, &root, &child, &root_x, &root_y, &win_x, &win_y, &mask ); printf("Root: %d,%d\n", root_x, root_y); printf("Win: %d,%d\n", win_x, win_y); */ } }
void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { // 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. if (xkeyevent->type != KeyPress) { // 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 ) { KeySym rk; nbytes=XLookupString((XKeyEvent*)&peek_event, str, 256, &rk, NULL); if (rk==keysym_keycode) { XEvent event; XNextEvent(x11_display, &event); //erase next event handle_key_event( (XKeyEvent*)&event,true ); return; //ignore current, echo next } } // use the time from peek_event so it always works } // 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=p_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); }
/** * clutter_win32_handle_event: * @msg: A pointer to a structure describing a Win32 message. * * This function processes a single Win32 message. It can be used to * hook into external windows message processing (for example, a GDK * filter function). * * If clutter_win32_disable_event_retrieval() has been called, you must * let this function process events to update Clutter's internal state. * * Return value: %TRUE if the message was handled entirely by Clutter * and no further processing (such as calling the default window * procedure) should take place. %FALSE is returned if is the message * was not handled at all or if Clutter expects processing to take * place. * * Since: 1.6 */ gboolean clutter_win32_handle_event (const MSG *msg) { ClutterBackendWin32 *backend_win32; ClutterStageWin32 *stage_win32; ClutterDeviceManager *manager; ClutterInputDevice *core_pointer, *core_keyboard; ClutterStage *stage; ClutterStageWindow *impl; gboolean return_value = FALSE; stage = clutter_win32_get_stage_from_window (msg->hwnd); /* Ignore any messages for windows which we don't have a stage for */ if (stage == NULL) return FALSE; impl = _clutter_stage_get_window (stage); stage_win32 = CLUTTER_STAGE_WIN32 (impl); backend_win32 = stage_win32->backend; manager = clutter_device_manager_get_default (); core_pointer = clutter_device_manager_get_core_device (manager, CLUTTER_POINTER_DEVICE); core_keyboard = clutter_device_manager_get_core_device (manager, CLUTTER_KEYBOARD_DEVICE); switch (msg->message) { case WM_SIZE: if (!stage_win32->is_foreign_win /* Ignore size changes resulting from the stage being minimized - otherwise the window size will be set to 0,0 */ && msg->wParam != SIZE_MINIMIZED) { WORD new_width = LOWORD (msg->lParam); WORD new_height = HIWORD (msg->lParam); gfloat old_width, old_height; clutter_actor_get_size (CLUTTER_ACTOR (stage), &old_width, &old_height); if (new_width != old_width || new_height != old_height) clutter_actor_set_size (CLUTTER_ACTOR (stage), new_width, new_height); } break; case WM_SHOWWINDOW: if (msg->wParam) clutter_stage_win32_map (stage_win32); else clutter_stage_win32_unmap (stage_win32); break; case WM_ACTIVATE: if (msg->wParam == WA_INACTIVE) { if (stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED) { ClutterEvent *event = clutter_event_new (CLUTTER_STAGE_STATE); stage_win32->state &= ~CLUTTER_STAGE_STATE_ACTIVATED; event->any.stage = stage; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_win32->state; take_and_queue_event (event); } } else if (!(stage_win32->state & CLUTTER_STAGE_STATE_ACTIVATED)) { ClutterEvent *event = clutter_event_new (CLUTTER_STAGE_STATE); stage_win32->state |= CLUTTER_STAGE_STATE_ACTIVATED; event->any.stage = stage; event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; event->stage_state.new_state = stage_win32->state; take_and_queue_event (event); } break; case WM_PAINT: CLUTTER_NOTE (MULTISTAGE, "expose for stage:%p, redrawing", stage); clutter_redraw (stage); break; case WM_DESTROY: { ClutterEvent *event = clutter_event_new (CLUTTER_DESTROY_NOTIFY); CLUTTER_NOTE (EVENT, "WM_DESTROY"); event->any.stage = stage; take_and_queue_event (event); } break; case WM_CLOSE: { ClutterEvent *event = clutter_event_new (CLUTTER_DELETE); CLUTTER_NOTE (EVENT, "WM_CLOSE"); event->any.stage = stage; take_and_queue_event (event); /* The default window proc will destroy the window so we want to prevent this to allow applications to optionally destroy the window themselves */ return_value = TRUE; } break; case WM_LBUTTONDOWN: make_button_event (msg, stage, 1, 1, FALSE, core_pointer); break; case WM_MBUTTONDOWN: make_button_event (msg, stage, 2, 1, FALSE, core_pointer); break; case WM_RBUTTONDOWN: make_button_event (msg, stage, 3, 1, FALSE, core_pointer); break; case WM_LBUTTONUP: make_button_event (msg, stage, 1, 1, TRUE, core_pointer); break; case WM_MBUTTONUP: make_button_event (msg, stage, 2, 1, TRUE, core_pointer); break; case WM_RBUTTONUP: make_button_event (msg, stage, 3, 1, TRUE, core_pointer); break; case WM_LBUTTONDBLCLK: make_button_event (msg, stage, 1, 2, FALSE, core_pointer); break; case WM_MBUTTONDBLCLK: make_button_event (msg, stage, 2, 2, FALSE, core_pointer); break; case WM_RBUTTONDBLCLK: make_button_event (msg, stage, 3, 2, FALSE, core_pointer); break; case WM_MOUSEWHEEL: stage_win32->scroll_pos += (SHORT) HIWORD (msg->wParam); while (abs (stage_win32->scroll_pos) >= WHEEL_DELTA) { ClutterEvent *event = clutter_event_new (CLUTTER_SCROLL); POINT pt; event->scroll.time = msg->time; event->scroll.modifier_state = get_modifier_state (LOWORD (msg->wParam)); event->any.stage = stage; clutter_event_set_device (event, core_pointer); /* conversion to window coordinates is required */ pt.x = GET_X_LPARAM (msg->lParam); pt.y = GET_Y_LPARAM (msg->lParam); ScreenToClient (msg->hwnd, &pt); event->scroll.x = pt.x; event->scroll.y = pt.y; if (stage_win32->scroll_pos > 0) { event->scroll.direction = CLUTTER_SCROLL_UP; stage_win32->scroll_pos -= WHEEL_DELTA; } else { event->scroll.direction = CLUTTER_SCROLL_DOWN; stage_win32->scroll_pos += WHEEL_DELTA; } take_and_queue_event (event); } break; case WM_MOUSEMOVE: { ClutterEvent *event = clutter_event_new (CLUTTER_MOTION); event->motion.time = msg->time; event->motion.x = GET_X_LPARAM (msg->lParam); event->motion.y = GET_Y_LPARAM (msg->lParam); event->motion.modifier_state = get_modifier_state (msg->wParam); event->any.stage = stage; clutter_event_set_device (event, core_pointer); /* We need to start tracking when the mouse enters the stage if we're not already */ if (!stage_win32->tracking_mouse) { ClutterEvent *crossing = clutter_event_new (CLUTTER_ENTER); TRACKMOUSEEVENT tmevent; tmevent.cbSize = sizeof (tmevent); tmevent.dwFlags = TME_LEAVE; tmevent.hwndTrack = stage_win32->hwnd; TrackMouseEvent (&tmevent); event->crossing.time = msg->time; event->crossing.x = event->motion.x; event->crossing.y = event->motion.y; event->crossing.stage = stage; event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.related = NULL; clutter_event_set_device (event, core_pointer); /* we entered the stage */ _clutter_stage_add_device (stage, event->crossing.device); take_and_queue_event (crossing); stage_win32->tracking_mouse = TRUE; } take_and_queue_event (event); } break; case WM_MOUSELEAVE: { ClutterEvent *event = clutter_event_new (CLUTTER_LEAVE); event->crossing.time = msg->time; event->crossing.x = msg->pt.x; event->crossing.y = msg->pt.y; event->crossing.stage = stage; event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.related = NULL; clutter_event_set_device (event, core_pointer); /* we left the stage */ _clutter_stage_remove_device (stage, core_pointer); /* When we get a leave message the mouse tracking is automatically cancelled so we'll need to start it again when the mouse next enters the window */ stage_win32->tracking_mouse = FALSE; take_and_queue_event (event); } break; case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP: { ClutterEvent *event = clutter_event_new (CLUTTER_EVENT_NONE); int scan_code = (msg->lParam >> 16) & 0xff; int min = 0, max = CLUTTER_WIN32_KEY_MAP_SIZE, mid; BYTE key_states[256]; /* Get the keyboard modifier states. GetKeyboardState conveniently gets the key state that was current when the last keyboard message read was generated */ GetKeyboardState(key_states); /* Binary chop to check if we have a direct mapping for this key code */ while (min < max) { mid = (min + max) / 2; if (clutter_win32_key_map[mid].win_sym == msg->wParam) { event->key.keyval = clutter_win32_key_map[mid].clutter_sym; event->key.unicode_value = 0; break; } else if (clutter_win32_key_map[mid].win_sym < msg->wParam) min = mid + 1; else max = mid; } /* If we don't have a direct mapping then try getting the unicode value of the key sym */ if (min >= max) { WCHAR ch; BYTE shift_state[256]; /* Translate to a Unicode value, but only take into account the shift key. That way Ctrl+Shift+C will generate a capital C virtual key code with a zero unicode value for example */ memset (shift_state, 0, 256); shift_state[VK_SHIFT] = key_states[VK_SHIFT]; shift_state[VK_LSHIFT] = key_states[VK_LSHIFT]; shift_state[VK_RSHIFT] = key_states[VK_RSHIFT]; shift_state[VK_CAPITAL] = key_states[VK_CAPITAL]; if (ToUnicode (msg->wParam, scan_code, shift_state, &ch, 1, 0) == 1 /* The codes in this range directly match the Latin 1 codes so we can just use the Unicode value as the key sym */ && ch >= 0x20 && ch <= 0xff) event->key.keyval = ch; else /* Otherwise we don't know what the key means but the application might be able to do something with the scan code so we might as well still generate the event */ event->key.keyval = CLUTTER_KEY_VoidSymbol; /* Get the unicode value of the keypress again using the full modifier state */ if (ToUnicode (msg->wParam, scan_code, key_states, &ch, 1, 0) == 1) event->key.unicode_value = ch; else event->key.unicode_value = 0; } event->key.type = msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE; event->key.time = msg->time; event->key.modifier_state = get_key_modifier_state (key_states); event->key.hardware_keycode = scan_code; event->any.stage = stage; clutter_event_set_device (event, core_keyboard); take_and_queue_event (event); } break; case WM_GETMINMAXINFO: { MINMAXINFO *min_max_info = (MINMAXINFO *) msg->lParam; _clutter_stage_win32_get_min_max_info (stage_win32, min_max_info); return_value = TRUE; } break; case WM_SETCURSOR: /* If the cursor is in the window's client area and the stage's cursor should be invisible then we'll set a blank cursor instead */ if (LOWORD (msg->lParam) == HTCLIENT && !stage_win32->is_cursor_visible) { return_value = TRUE; _clutter_stage_win32_update_cursor (stage_win32); } break; } return return_value; }