#include "tkInt.h" #include "SdlTk.h" #include "SdlTkInt.h" #ifdef ANDROID #include #endif #undef TRACE_EVENTS #ifdef TRACE_EVENTS #ifdef ANDROID #define EVLOG(...) __android_log_print(ANDROID_LOG_ERROR,"SDLEV",__VA_ARGS__) #else #define EVLOG(...) fprintf(stderr,__VA_ARGS__); fputc('\n', stderr) #endif #else #define EVLOG(...) #endif SdlTkXInfo SdlTkX = { 0, 0, NULL }; /* *---------------------------------------------------------------------- * * SendAppEvent -- * * Send virtual application events to all toplevel windows * recursively. * * Results: * First window to receive application event. * *---------------------------------------------------------------------- */ static _Window * SendAppEvent(XEvent *event, int *sentp, _Window *_w) { _Window *result = NULL; while (_w != NULL) { if (_w->tkwin != NULL && (_w->tkwin->flags & TK_APP_TOP_LEVEL)) { *sentp += 1; if (*sentp == 1) { result = _w; } else { event->xany.serial = _w->display->request; event->xany.display = _w->display; event->xany.window = (Window) _w; SdlTkQueueEvent(event); } } if (_w->child != NULL) { _Window *tmp = SendAppEvent(event, sentp, _w->child); if (result == NULL && tmp != NULL) { result = tmp; } } _w = _w->next; } if (result != NULL) { event->xany.serial = result->display->request; event->xany.display = result->display; } return result; } /* *---------------------------------------------------------------------- * * SdlTkSendViewportUpdate -- * * Send virtual event <> to all toplevel windows. * * Results: * None. * *---------------------------------------------------------------------- */ void SdlTkSendViewportUpdate(void) { int nsent = 0; union { XEvent xe; XVirtualEvent ve; } event; memset(&event, 0, sizeof (event)); event.xe.xany.type = VirtualEvent; event.xe.xany.send_event = False; event.xe.xany.window = (Window) SdlTkX.screen->root; event.xe.xbutton.root = (Window) SdlTkX.screen->root; event.xe.xany.display = SdlTkX.display; event.xe.xany.serial = SdlTkX.display->request; event.xe.xbutton.x = SdlTkX.viewport.x; event.xe.xbutton.y = SdlTkX.viewport.y; event.xe.xbutton.x_root = SdlTkX.viewport.w; event.xe.xbutton.y_root = SdlTkX.viewport.h; event.xe.xbutton.time = SdlTkX.time_count; event.xe.xbutton.state = (int) SDL_ceil(SdlTkX.scale * 10000); event.ve.name = (Tk_Uid) "ViewportUpdate"; /* only TK_APP_TOP_LEVEL windows will get this */ event.xe.xany.window = (Window) SendAppEvent(&event.xe, &nsent, ((_Window *) SdlTkX.screen->root)->child); if (nsent > 0) { SdlTkQueueEvent(&event.xe); } } /* *---------------------------------------------------------------------- * * SendPointerUpdate -- * * Send XUpdatePointerUpdate with mouse position and state. * The XNextEvent routine calls Tk_UpdatePointer() * with the event information. * * Results: * None. * *---------------------------------------------------------------------- */ static void SendPointerUpdate(Tk_Window tkwin, int x, int y, int state) { union { XEvent xe; XUpdatePointerEvent pe; } event; memset(&event, 0, sizeof (event)); event.pe.type = PointerUpdate; event.pe.serial = Tk_Display(tkwin)->request; event.pe.display = Tk_Display(tkwin); event.pe.window = Tk_WindowId(tkwin); event.pe.send_event = False; event.pe.x = x; event.pe.y = y; event.pe.state = state; event.pe.tkwin = tkwin; SdlTkQueueEvent(&event.xe); } /* *---------------------------------------------------------------------- * * SdlTkAttachTkWindow -- * * Associates a TkWindow structure with the given X window. * * Results: * None. * * Side effects: * Sets the tkwin field of the given _Window. Allocates a copy of * the window name, if any. * *---------------------------------------------------------------------- */ void SdlTkAttachTkWindow(Window w, TkWindow *tkwin) { _Window *_w = (_Window *) w; _w->tkwin = tkwin; } #ifdef ANDROID /* *---------------------------------------------------------------------- * * ConfigGLWindows -- * * Generate event for flagged windows with GL context. * * Results: * None. * * Side effects: * A window event is added to the event queue. * *---------------------------------------------------------------------- */ static void ConfigGLWindows(Window w) { _Window *_w = (_Window *) w; _w = _w->child; while (_w != NULL) { ConfigGLWindows((Window) _w); if (_w->gl_flags & 1) { _w->gl_flags &= ~1; if (_w->atts.map_state != IsUnmapped) { SdlTkGenerateConfigureNotify((Window ) _w); } } _w = _w->next; } } #endif /* *---------------------------------------------------------------------- * * SdlTkScreenChanged, SdlTkScreenRefresh -- * * Redraw entire display. * * Results: * None. * *---------------------------------------------------------------------- */ void SdlTkScreenChanged(void) { SdlTkX.draw_later |= SDLTKX_DRAW; } void SdlTkScreenRefresh(void) { _Window *child, *focus_window; Region tmpRgn; #ifdef ANDROID SDL_GLContext currgl; #endif if ((SdlTkX.draw_later & (SDLTKX_DRAW | SDLTKX_DRAWALL | SDLTKX_PRESENT)) == SDLTKX_PRESENT) { SdlTkGfxPresent(SdlTkX.sdlrend, SdlTkX.sdltex); SdlTkX.draw_later &= ~SDLTKX_PRESENT; } if ((SdlTkX.draw_later & (SDLTKX_DRAW | SDLTKX_DRAWALL)) == 0) { return; } #ifdef ANDROID /* Can happen when app is/gets paused. */ if ((currgl = SDL_GL_GetCurrentContext()) == NULL) { EVLOG("SdlTkScreenRefresh: GL context is NULL"); return; } /* Detect that GL context has changed. */ if (SdlTkX.gl_context != NULL && currgl != SdlTkX.gl_context) { SDL_Texture *newtex; EVLOG("SdlTkScreenRefresh: GL context switching %p -> %p", currgl, SdlTkX.gl_context); newtex = SDL_CreateTexture(SdlTkX.sdlrend, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, SdlTkX.screen->width, SdlTkX.screen->height); if (newtex != NULL) { SDL_DestroyTexture(SdlTkX.sdltex); SdlTkX.sdltex = newtex; SdlTkX.gl_context = currgl; } else { return; } } else if (SdlTkX.gl_context == NULL) { SdlTkX.gl_context = currgl; } #endif tmpRgn = SdlTkRgnPoolGet(); if (SdlTkX.screen_update_region == NULL) { SdlTkX.screen_update_region = SdlTkRgnPoolGet(); } /* If areas of the root window were exposed, paint them now */ if (SdlTkX.screen_dirty_region && !XEmptyRegion(SdlTkX.screen_dirty_region)) { Uint32 pixel = SDL_MapRGB(SdlTkX.sdlsurf->format, #ifdef ANDROID 0x00, 0x00, 0x00 #else 0x00, 0x4E, 0x98 #endif ); SdlTkGfxFillRegion(SdlTkX.screen->root, SdlTkX.screen_dirty_region, pixel); XUnionRegion(SdlTkX.screen_dirty_region, SdlTkX.screen_update_region, SdlTkX.screen_update_region); XSetEmptyRegion(SdlTkX.screen_dirty_region); } if (SdlTkX.draw_later & SDLTKX_DRAWALL) { XRectangle r; SdlTkX.draw_later &= ~SDLTKX_DRAWALL; r.x = r.y = 0; r.width = SdlTkX.screen->width; r.height = SdlTkX.screen->height; XUnionRectWithRegion(&r, SdlTkX.screen_update_region, SdlTkX.screen_update_region); } focus_window = (_Window *) SdlTkX.focus_window_not_override; if (focus_window != NULL) { child = focus_window->parent; while (child != NULL && child->dec == NULL) { child = child->parent; } if (child != NULL) { focus_window = child->child; } } /* Check each toplevel from highest to lowest */ for (child = ((_Window *) SdlTkX.screen->root)->child; child != NULL; child = child->next) { if (child->atts.map_state == IsUnmapped) { continue; } /* Keep track of which decframe was the "active" one. Redraw frames * when changes occur. */ if (child->dec != NULL) { if (child->child == (_Window *) focus_window && !SdlTkDecSetActive(child, -1) && SdlTkX.sdlfocus) { SdlTkDecSetDraw(child, 1); SdlTkDecSetActive(child, 1); } else if (child->child != (_Window *) focus_window && SdlTkDecSetActive(child, -1)) { SdlTkDecSetDraw(child, 1); SdlTkDecSetActive(child, 0); } if (SdlTkDecSetDraw(child, -1)) { SdlTkDecDrawFrame(child); XUnionRegion(child->visRgn, child->dirtyRgn, child->dirtyRgn); SdlTkDecSetDraw(child, 0); } } if (!XEmptyRegion(child->dirtyRgn)) { XIntersectRegion(child->dirtyRgn, child->visRgnInParent, tmpRgn); /* Add those areas to SdlTkX.screen_update_region. */ XOffsetRegion(tmpRgn, child->atts.x, child->atts.y); XUnionRegion(tmpRgn, SdlTkX.screen_update_region, SdlTkX.screen_update_region); XSetEmptyRegion(child->dirtyRgn); } } SdlTkRgnPoolFree(tmpRgn); #ifdef ANDROID if (!SdlTkX.in_background) { SdlTkGfxUpdateRegion(SdlTkX.sdlrend, SdlTkX.sdltex, SdlTkX.sdlsurf, SdlTkX.screen_update_region); } #else SdlTkGfxUpdateRegion(SdlTkX.sdlrend, SdlTkX.sdltex, SdlTkX.sdlsurf, SdlTkX.screen_update_region); #endif XSetEmptyRegion(SdlTkX.screen_update_region); SdlTkX.draw_later &= ~(SDLTKX_DRAW | SDLTKX_RENDCLR | SDLTKX_PRESENT); SdlTkX.frame_count++; } /* *---------------------------------------------------------------------- * * AddToAccelRing -- * * Add accelerometer value to ring buffer given axis. * Returns the delta to the last accelerometer value. * *---------------------------------------------------------------------- */ #ifdef ANDROID static int AddToAccelRing(int time, short value, int axis) { AccelRing *rp; int i, imax, dt, dv = 0; if ((axis < 0) || (axis > 2)) { return dv; } rp = &SdlTkX.accel_ring[axis]; imax = sizeof (rp->values) / sizeof (rp->values[0]); dt = (time - rp->time) / (1000 / SDLTK_FRAMERATE); if (dt >= imax) { for (i = 0; i < imax; i++) { rp->values[i] = value; } rp->index = 0; rp->time = time; return dv; } if (dt <= 0) { dv = value - rp->values[rp->index]; /* Don't update index and time fields. */ } else { int prevval = rp->values[rp->index]; if (dt > 1) { /* Linear interpolate missing values. */ prevval <<= 8; dv = ((value << 8) - prevval) / dt; for (i = 0; i < dt - 1; i++) { prevval += dv; rp->index++; if (rp->index >= imax) { rp->index = 0; } rp->values[rp->index] = prevval >> 8; } prevval >>= 8; } dv = value - prevval; rp->index++; if (rp->index >= imax) { rp->index = 0; } rp->time = time; } rp->values[rp->index] = value; return dv; } #endif /* *---------------------------------------------------------------------- * * TranslatePointer, TranslateFinger -- * * Translate device (screen) coordinates to X11 coordinates * and vice versa. * *---------------------------------------------------------------------- */ static void TranslatePointer(int rev, int *x, int *y) { if (SdlTkX.draw_later & SDLTKX_SCALED) { if (rev) { /* X to screen */ *x = (int) ((*x - SdlTkX.viewport.x) * SdlTkX.scale); *y = (int) ((*y - SdlTkX.viewport.y) * SdlTkX.scale); if (SdlTkX.outrect) { *x += SdlTkX.outrect->x; *y += SdlTkX.outrect->y; } } else { /* screen to X */ if (SdlTkX.outrect) { *x -= SdlTkX.outrect->x; *y -= SdlTkX.outrect->y; } *x = (int) (*x / SdlTkX.scale) + SdlTkX.viewport.x; *y = (int) (*y / SdlTkX.scale) + SdlTkX.viewport.y; } } else if (SdlTkX.outrect) { if (rev) { /* X to screen */ *x += SdlTkX.outrect->x; *y += SdlTkX.outrect->y; } else { /* screen to X */ *x -= SdlTkX.outrect->x; *y -= SdlTkX.outrect->y; } } } int SdlTkGetMouseState(int *x, int *y) { int state; state = SDL_GetMouseState(x, y); TranslatePointer(0, x, y); return state; } static void FingerToScreen(SDL_Event *in, int *x, int *y) { int sw, sh; SDL_GetWindowSize(SdlTkX.sdlscreen, &sw, &sh); *x = (int) (in->tfinger.x * sw); *y = (int) (in->tfinger.y * sh); TranslatePointer(0, x, y); } #ifdef ANDROID static void TranslateFinger(SDL_Event *in, SDL_Event *out) { *out = *in; if (SdlTkX.draw_later & SDLTKX_SCALED) { int x, y, sw, sh, w, h; SDL_GetWindowSize(SdlTkX.sdlscreen, &sw, &sh); x = (int) (out->tfinger.x * sw); y = (int) (out->tfinger.y * sh); TranslatePointer(0, &x, &y); if (SdlTkX.root_w) { w = SdlTkX.root_w; h = SdlTkX.root_h; } else { w = SdlTkX.sdlsurf->w; h = SdlTkX.sdlsurf->h; } out->tfinger.x = (float) x / w; out->tfinger.y = (float) y / h; out->tfinger.dx /= SdlTkX.scale; out->tfinger.dy /= SdlTkX.scale; } } #endif #ifdef ANDROID /* *---------------------------------------------------------------------- * * Data and code to translate touch events to middle/right mouse * button and motion events. * *---------------------------------------------------------------------- */ #define TRANSLATE_RMB 1 #define TRANSLATE_PTZ 2 #define TRANSLATE_ZOOM 4 #define TRANSLATE_FINGER 8 #define TRANSLATE_FBTNS 16 static struct TranslateInfo { int enabled; unsigned long now, when; void (*function)(ClientData); int state; int count; int fingerBits; int nFingers; int pinchDelta; double pinchDist; int pinchX, pinchY; SDL_Event sdl_event; SDL_Event mmb_event; SDL_TouchFingerEvent finger[10]; } TranslateInfo = { TRANSLATE_RMB | TRANSLATE_ZOOM | TRANSLATE_FINGER, 0, 0, NULL, 0, 0, 0, }; static void TranslateStop(void) { struct TranslateInfo *info = &TranslateInfo; info->function = NULL; info->count = 0; info->state = 0; } static void TranslateTimer(ClientData clientData) { struct TranslateInfo *info = (struct TranslateInfo *) clientData; SDL_Event e; EVLOG(" TIMER#0 FIRED"); e = info->sdl_event; e.button.type = SDL_MOUSEBUTTONDOWN; e.button.button = SDL_BUTTON_RIGHT; e.button.state = SDL_PRESSED; SDL_PeepEvents(&e, 1, SDL_ADDEVENT, 0, 0); } static void TranslateTimer1(ClientData clientData) { struct TranslateInfo *info = (struct TranslateInfo *) clientData; EVLOG(" TIMER#1 FIRED"); info->function = TranslateTimer; info->when = info->now + 900; info->state = 5; EVLOG(" TIMER ON ST=%d", info->state); SDL_PeepEvents(&info->sdl_event, 1, SDL_ADDEVENT, 0, 0); } static void TranslateTimer2(ClientData clientData) { struct TranslateInfo *info = (struct TranslateInfo *) clientData; EVLOG(" TIMER#2 FIRED"); if (--info->count <= 0) { info->count = 0; info->state = 0; } else { int dx, dy; dx = (info->mmb_event.motion.xrel * 9) / 100; dx = (dx < 0) ? -dx : dx; dx = (dx < 1) ? 1 : dx; dy = (info->mmb_event.motion.yrel * 9) / 100; dy = (dy < 0) ? -dy : dy; dy = (dy < 1) ? 1 : dy; if (info->mmb_event.motion.xrel > 0) { info->mmb_event.motion.xrel -= dx; if (info->mmb_event.motion.xrel < 0) { info->mmb_event.motion.xrel = 1; } } else if (info->mmb_event.motion.xrel < 0) { info->mmb_event.motion.xrel += dx; if (info->mmb_event.motion.xrel > 0) { info->mmb_event.motion.xrel = -1; } } if (info->mmb_event.motion.yrel > 0) { info->mmb_event.motion.yrel -= dy; if (info->mmb_event.motion.yrel < 0) { info->mmb_event.motion.yrel = 1; } } else if (info->mmb_event.motion.yrel < 0) { info->mmb_event.motion.yrel += dy; if (info->mmb_event.motion.yrel > 0) { info->mmb_event.motion.yrel = -1; } } info->mmb_event.motion.x += info->mmb_event.motion.xrel; info->mmb_event.motion.y += info->mmb_event.motion.yrel; EVLOG(" MOUSEMOTION X=%d Y=%d ID=%d S=%d dx=%d dy=%d", info->mmb_event.motion.x, info->mmb_event.motion.y, info->mmb_event.motion.which, info->mmb_event.motion.state, info->mmb_event.motion.xrel, info->mmb_event.motion.yrel); if (info->mmb_event.motion.xrel || info->mmb_event.motion.yrel) { SDL_PeepEvents(&info->mmb_event, 1, SDL_ADDEVENT, 0, 0); } else { info->count = 0; info->state = 0; } } if (info->state) { info->function = TranslateTimer2; info->when = info->now + 100; EVLOG(" TIMER ON ST=%d", info->state); } else { SDL_Event mmb; mmb.type = SDL_MOUSEBUTTONUP; mmb.button.which = info->mmb_event.motion.which; mmb.button.button = SDL_BUTTON_MIDDLE; mmb.button.state = SDL_RELEASED; mmb.button.x = info->mmb_event.motion.x; mmb.button.y = info->mmb_event.motion.y; SDL_PeepEvents(&mmb, 1, SDL_ADDEVENT, 0, 0); } } #endif static int FixKeyCode(int ch) { if (ch >= 'a' && ch <= 'z') { return SDL_SCANCODE_A + ch - 'a'; } else if (ch >= 'A' && ch <= 'Z') { return SDL_SCANCODE_A + ch - 'A'; } switch (ch) { case '0': return SDL_SCANCODE_0; case '1': return SDL_SCANCODE_1; case '2': return SDL_SCANCODE_2; case '3': return SDL_SCANCODE_3; case '4': return SDL_SCANCODE_4; case '5': return SDL_SCANCODE_5; case '6': return SDL_SCANCODE_6; case '7': return SDL_SCANCODE_7; case '8': return SDL_SCANCODE_8; case '9': return SDL_SCANCODE_9; case ' ': return SDL_SCANCODE_SPACE; case ',': return SDL_SCANCODE_COMMA; case '.': return SDL_SCANCODE_PERIOD; case '/': return SDL_SCANCODE_SLASH; case '`': return SDL_SCANCODE_GRAVE; case ';': return SDL_SCANCODE_SEMICOLON; case '\'': return SDL_SCANCODE_APOSTROPHE; case '\\': return SDL_SCANCODE_BACKSLASH; case '\r': return SDL_SCANCODE_RETURN; case '\010': return SDL_SCANCODE_BACKSPACE; case '\t': return SDL_SCANCODE_TAB; case '\033': return SDL_SCANCODE_ESCAPE; case '-': return SDL_SCANCODE_MINUS; case '=': return SDL_SCANCODE_EQUALS; case '[': return SDL_SCANCODE_LEFTBRACKET; case ']': return SDL_SCANCODE_RIGHTBRACKET; } return 0; } static int MkTransChars(XKeyEvent *ev) { int ch = 0; if (ev->keycode >= SDL_SCANCODE_A && ev->keycode <= SDL_SCANCODE_Z) { ev->trans_chars[0] = ev->keycode - SDL_SCANCODE_A + 'a'; ev->nbytes = 1; return 1; } switch (ev->keycode) { case SDL_SCANCODE_0: ch = '0'; break; case SDL_SCANCODE_1: ch = '1'; break; case SDL_SCANCODE_2: ch = '2'; break; case SDL_SCANCODE_3: ch = '3'; break; case SDL_SCANCODE_4: ch = '4'; break; case SDL_SCANCODE_5: ch = '5'; break; case SDL_SCANCODE_6: ch = '6'; break; case SDL_SCANCODE_7: ch = '7'; break; case SDL_SCANCODE_8: ch = '8'; break; case SDL_SCANCODE_9: ch = '9'; break; case SDL_SCANCODE_SPACE: ch = ' '; break; case SDL_SCANCODE_COMMA: ch = ','; break; case SDL_SCANCODE_PERIOD: ch = '.'; break; case SDL_SCANCODE_SLASH: ch = '/'; break; case SDL_SCANCODE_GRAVE: ch = '`'; break; case SDL_SCANCODE_SEMICOLON: ch = ';'; break; case SDL_SCANCODE_APOSTROPHE: ch = '\''; break; case SDL_SCANCODE_BACKSLASH: ch = '\\'; break; case SDL_SCANCODE_MINUS: ch = '-'; break; case SDL_SCANCODE_EQUALS: ch = '='; break; case SDL_SCANCODE_LEFTBRACKET: ch = '['; break; case SDL_SCANCODE_RIGHTBRACKET: ch = ']'; break; } if (ch) { ev->trans_chars[0] = ch; ev->nbytes = 1; return 1; } return 0; } static int ProcessTextInput(XEvent *event, int sdl_mod, const char *text, int len) { int i, n, n2, ulen = Tcl_NumUtfChars(text, len); char buf[TCL_UTF_MAX]; if (ulen <= 0) { return 0; } if (sdl_mod & KMOD_RALT) { event->xkey.state &= ~Mod4Mask; } for (i = 0; i < ulen; i++) { Tcl_UniChar ch; n = Tcl_UtfToUniChar(text, &ch); n2 = 0; /* Deal with surrogate pairs */ if ((ch >= 0xd800) && (ch <= 0xdbff)) { Tcl_UniChar ch2; if (i + 1 < ulen) { n2 = Tcl_UtfToUniChar(text + n, &ch2); if ((ch2 >= 0xdc00) && (ch2 <= 0xdfff)) { #if TCL_UTF_MAX > 4 ch = ((ch & 0x3ff) << 10) | (ch2 & 0x3ff); ch += 0x10000; #else ch = 0xfffd; #endif ++i; } else { ch = 0xfffd; n2 = 0; } } else { ch = 0xfffd; } } else if (((ch >= 0xdc00) && (ch <= 0xdfff)) || (ch == 0xfffe) || (ch == 0xffff)) { ch = 0xfffd; } event->xkey.nbytes = Tcl_UniCharToUtf(ch, buf); event->xkey.time = SdlTkX.time_count; if (event->xkey.nbytes > sizeof (event->xkey.trans_chars)) { event->xkey.nbytes = sizeof (event->xkey.trans_chars); } memcpy(event->xkey.trans_chars, buf, event->xkey.nbytes); if (len == 1) { event->xkey.keycode = FixKeyCode(event->xkey.trans_chars[0]); } else { event->xkey.keycode = -1; } text += n + n2; /* Queue the KeyPress */ EVLOG(" KEYPRESS: CODE=0x%X UC=0x%X", event->xkey.keycode, ch); event->type = KeyPress; SdlTkQueueEvent(event); /* Queue the KeyRelease except for the last */ event->type = KeyRelease; if (i < ulen - 1) { SdlTkQueueEvent(event); } } return 1; } /* *---------------------------------------------------------------------- * * SdlTkTranslateEvent -- * * Turn an SDL_Event into an XEvent. * * Results: * Return 1 if the SDL event was translated into an XEvent; * otherwise return 0. * * Side effects: * Pointer events other than MouseWheelEvent are handled by * Tk_UpdatePointer(), which has various side effects. * If the event type is SDL_QUIT, Tcl_Exit() is called. * *---------------------------------------------------------------------- */ int SdlTkTranslateEvent(SDL_Event *sdl_event, XEvent *event, unsigned long now_ms) { int x, y, state = 0; SDL_Event fix_sdl_event; SDL_Event txt_sdl_event; const char *evname = NULL; #ifdef ANDROID struct TranslateInfo *info = &TranslateInfo; int doFinger = 0; SDL_Event tmp_event; #endif #ifdef ANDROID #ifdef TRACE_EVENTS if (sdl_event->type < SDL_USEREVENT) { if ((sdl_event->type != SDL_JOYAXISMOTION) || SdlTkX.accel_enabled || (sdl_event->jaxis.which != SdlTkX.accel_id)) { EVLOG(" T=%ld dt=%ld ST=%d", now_ms, now_ms - info->now, info->state); } } #endif /* Handle timer */ info->now = now_ms; if (info->function != NULL) { if ((long) info->when - (long) info->now <= 0) { void (*function)(ClientData) = info->function; info->function = NULL; function((ClientData) info); } } #ifdef TRACE_EVENTS switch (sdl_event->type) { case SDL_MOUSEBUTTONUP: EVLOG("EV=MOUSEUP X=%d Y=%d ID=%d B=%d", sdl_event->button.x, sdl_event->button.y, sdl_event->button.which, sdl_event->button.button); break; case SDL_MOUSEBUTTONDOWN: EVLOG("EV=MOUSEDOWN X=%d Y=%d ID=%d B=%d", sdl_event->button.x, sdl_event->button.y, sdl_event->button.which, sdl_event->button.button); break; case SDL_MOUSEMOTION: EVLOG("EV=MOUSEMOTION X=%d Y=%d ID=%d S=%d dx=%d dy=%d", sdl_event->motion.x, sdl_event->motion.y, sdl_event->motion.which, sdl_event->motion.state, sdl_event->motion.xrel, sdl_event->motion.yrel); break; case SDL_MOUSEWHEEL: EVLOG("EV=MOUSEWHEEL X=%d Y=%d ID=%d", sdl_event->wheel.x, sdl_event->wheel.y, sdl_event->wheel.which); break; case SDL_FINGERDOWN: EVLOG("EV=FINGERDOWN X=%g Y=%g ID=%lld dx=%g dy=%g", sdl_event->tfinger.x, sdl_event->tfinger.y, sdl_event->tfinger.fingerId, sdl_event->tfinger.dx, sdl_event->tfinger.dy); break; case SDL_FINGERUP: EVLOG("EV=FINGERUP X=%g Y=%g ID=%lld dx=%g dy=%g", sdl_event->tfinger.x, sdl_event->tfinger.y, sdl_event->tfinger.fingerId, sdl_event->tfinger.dx, sdl_event->tfinger.dy); break; case SDL_FINGERMOTION: EVLOG("EV=FINGERMOTION X=%g Y=%g ID=%lld dx=%g dy=%g", sdl_event->tfinger.x, sdl_event->tfinger.y, sdl_event->tfinger.fingerId, sdl_event->tfinger.dx, sdl_event->tfinger.dy); break; case SDL_MULTIGESTURE: EVLOG("EV=MULTIGESTURE X=%g Y=%g NF=%d d=%g t=%g", sdl_event->mgesture.x, sdl_event->mgesture.y, sdl_event->mgesture.numFingers, sdl_event->mgesture.dTheta, sdl_event->mgesture.dDist); break; } #endif if (!(info->enabled & (TRANSLATE_PTZ | TRANSLATE_ZOOM))) { goto skipPZ; } /* * Pinch-to-zoom two finger detection. */ if (sdl_event->type == SDL_FINGERDOWN && sdl_event->tfinger.fingerId >= 0 && sdl_event->tfinger.fingerId < 10) { info->nFingers++; info->fingerBits |= 1 << sdl_event->tfinger.fingerId; info->finger[sdl_event->tfinger.fingerId] = sdl_event->tfinger; doFinger = ((info->fingerBits & 3) == 3) ? 2 : 0; } else if (sdl_event->type == SDL_FINGERUP && sdl_event->tfinger.fingerId >= 0 && sdl_event->tfinger.fingerId < 10) { int oldBits = info->fingerBits; info->nFingers--; info->fingerBits &= ~(1 << sdl_event->tfinger.fingerId); if (info->fingerBits == 0) { info->nFingers = 0; } if (info->nFingers < 2) { info->pinchDelta = 0; info->pinchDist = 0; info->pinchX = info->pinchY = 0; } info->finger[sdl_event->tfinger.fingerId] = sdl_event->tfinger; doFinger = sdl_event->tfinger.fingerId + 3; if ((oldBits & 3) != 3 || doFinger > 4) { doFinger = 0; } } else if (sdl_event->type == SDL_FINGERMOTION && sdl_event->tfinger.fingerId >= 0 && sdl_event->tfinger.fingerId < 10) { info->finger[sdl_event->tfinger.fingerId] = sdl_event->tfinger; doFinger = ((info->fingerBits & 3) == 3 && sdl_event->tfinger.fingerId >= 0 && sdl_event->tfinger.fingerId < 2) ? 1 : 0; } if (doFinger) { float dx, dy, cx, cy; double dist, phi; Tk_Window tkwin; int xx, yy, pdx = 0, pdy = 0, ddist = 0, needFingers; needFingers = (info->enabled & TRANSLATE_PTZ) ? 3 : 2; TranslateStop(); dx = info->finger[1].x - info->finger[0].x; dy = info->finger[1].y - info->finger[0].y; dist = sqrt(dx * dx + dy * dy); phi = atan2(dy, dx); cx = info->finger[0].x + dx / 2.0; cy = info->finger[0].y + dy / 2.0; EVLOG("EV=PINCHTOZOOM X=%g Y=%g DIST=%g PHI=%g", cx, cy, dist, phi); dx = SdlTkX.screen->width * dx; dy = SdlTkX.screen->height * dy; xx = sqrt(dx * dx + dy * dy); yy = 64 * 180 * phi / M_PI; x = SdlTkX.screen->width * cx; y = SdlTkX.screen->height * cy; if (info->pinchDelta) { ddist = xx - info->pinchDist; pdx = x - info->pinchX; pdy = info->pinchY - y; EVLOG(" dDIST=%d dX=%d dY=%d", ddist, pdx, pdy); } info->pinchDist = xx; info->pinchX = x; info->pinchY = y; if ((info->enabled & TRANSLATE_ZOOM) && info->pinchDelta && info->nFingers >= needFingers) { float dir = 0; if (info->nFingers == needFingers && ddist <= -SdlTkX.nearby_pixels / 4) { dir = 0.99; } else if (info->nFingers == needFingers && ddist >= SdlTkX.nearby_pixels / 4) { dir = 1 / 0.99; } if (dir) { SdlTkZoomInt(x, y, dir); } else if (info->nFingers > needFingers && (pdx <= -2 || pdx >= 2 || pdy <= -2 || pdy >= 2)) { SdlTkPanInt(-pdx, pdy); } goto skipTranslation; } info->pinchDelta = 1; if (!(info->enabled & TRANSLATE_PTZ)) { goto skipTranslation; } TranslatePointer(0, &x, &y); xx = (int) (xx / SdlTkX.scale); if (SdlTkX.mouse_window != NULL) { tkwin = (Tk_Window) SdlTkX.mouse_window->tkwin; } if (SdlTkX.capture_window != NULL) { if (tkwin != NULL && Tk_Display(tkwin) != SdlTkX.capture_window->display) { tkwin = (Tk_Window) SdlTkX.capture_window; } } if (SdlTkX.keyboard_window != NULL) { if (tkwin != NULL && Tk_Display(tkwin) != SdlTkX.keyboard_window->display) { tkwin = (Tk_Window) SdlTkX.keyboard_window->tkwin; } } if (tkwin != NULL) { memset(event, 0, sizeof (*event)); event->xany.type = VirtualEvent; event->xany.serial = Tk_Display(tkwin)->request; event->xany.send_event = False; event->xany.window = Tk_WindowId(tkwin); event->xbutton.root = (Window) SdlTkX.screen->root; event->xany.display = Tk_Display(tkwin); event->xbutton.x = xx; event->xbutton.y = yy; event->xbutton.x_root = x; event->xbutton.y_root = y; event->xbutton.time = now_ms; event->xbutton.state = doFinger - 1; ((XVirtualEvent *) event)->name = (Tk_Uid) "PinchToZoom"; SdlTkQueueEvent(event); goto skipTranslation; } } skipPZ: if (!(info->enabled & TRANSLATE_RMB)) { goto skipTranslation; } /* * Middle/right mouse button/motion emulation. */ if (info->fingerBits > 1) { TranslateStop(); goto skipTranslation; } if ((info->state & 8) && !((sdl_event->type == SDL_MOUSEMOTION && sdl_event->button.which == SDL_TOUCH_MOUSEID && sdl_event->button.button == SDL_BUTTON_MIDDLE) || (sdl_event->type == SDL_FINGERUP) || (sdl_event->type >= SDL_JOYAXISMOTION && sdl_event->type <= SDL_JOYDEVICEREMOVED) || (sdl_event->type < SDL_KEYDOWN) || (sdl_event->type > SDL_FINGERMOTION))) { info->function = NULL; info->count = 0; info->state &= ~8; } if (sdl_event->type == SDL_MOUSEBUTTONDOWN) { if (!(info->state & 1)) { info->function = NULL; if (info->state == 0 && sdl_event->button.which == SDL_TOUCH_MOUSEID && sdl_event->button.button == SDL_BUTTON_LEFT) { info->state = 1; info->sdl_event = *sdl_event; info->function = TranslateTimer1; info->when = info->now + 100; EVLOG(" TIMER ON ST=%d", info->state); return 0; } } else if (info->state == 0 && sdl_event->button.which == SDL_TOUCH_MOUSEID && sdl_event->button.button == SDL_BUTTON_LEFT) { return 0; } } else if (sdl_event->type == SDL_MOUSEMOTION && sdl_event->motion.which == SDL_TOUCH_MOUSEID) { int nearby = 21 * SdlTkX.nearby_pixels / 30; if (nearby < 5) { nearby = 5; } info->sdl_event.button.x = sdl_event->motion.x; info->sdl_event.button.y = sdl_event->motion.y; if ((info->state == 1) && (sdl_event->motion.xrel > nearby || sdl_event->motion.xrel < -nearby || sdl_event->motion.yrel > nearby || sdl_event->motion.yrel < -nearby)) { SDL_PeepEvents(sdl_event, 1, SDL_ADDEVENT, 0, 0); tmp_event = info->sdl_event; sdl_event = &tmp_event; sdl_event->button.button = SDL_BUTTON_MIDDLE; info->mmb_event = *sdl_event; info->state = 2; info->function = NULL; EVLOG(" TIMER OFF ST=%d", info->state); } else if (info->state & 2) { tmp_event = *sdl_event; sdl_event = &tmp_event; sdl_event->motion.state = SDL_BUTTON(SDL_BUTTON_MIDDLE); info->mmb_event = *sdl_event; } else if (info->state == 1) { return 0; } } else if (sdl_event->type == SDL_MOUSEBUTTONUP) { info->sdl_event.button.x = sdl_event->button.x; info->sdl_event.button.y = sdl_event->button.y; if ((info->state & 2) && sdl_event->button.which == SDL_TOUCH_MOUSEID && sdl_event->button.button == SDL_BUTTON_LEFT) { info->state = 8; info->count = 7; info->function = TranslateTimer2; info->when = info->now + 100; EVLOG(" TIMER ON ST=%d", info->state); return 0; } else if ((info->state & 1) && sdl_event->button.which == SDL_TOUCH_MOUSEID && sdl_event->button.button == SDL_BUTTON_LEFT) { if (info->state == 1) { SDL_PeepEvents(sdl_event, 1, SDL_ADDEVENT, 0, 0); sdl_event = &info->sdl_event; } else { SDL_PeepEvents(sdl_event, 1, SDL_ADDEVENT, 0, 0); tmp_event = *sdl_event; sdl_event = &tmp_event; sdl_event->button.button = SDL_BUTTON_RIGHT; } info->function = NULL; info->state = 0; EVLOG(" TIMER OFF ST=%d", info->state); } } skipTranslation: ; #endif switch (sdl_event->type) { case SDL_TEXTINPUT: case SDL_TEXTEDITING: case SDL_KEYDOWN: case SDL_KEYUP: { TkKeyEvent *kePtr = (TkKeyEvent *) event; Window fwin; kePtr->charValueLen = 0; kePtr->charValuePtr = NULL; state = SDL_GetMouseState(&x, &y); TranslatePointer(0, &x, &y); fwin = (SdlTkX.focus_window != None) ? SdlTkX.focus_window : (Window) SdlTkX.screen->root; event->type = (sdl_event->type == SDL_KEYUP) ? KeyRelease : KeyPress; event->xkey.serial = ((_Window *) fwin)->display->request; event->xkey.send_event = False; event->xkey.display = ((_Window *) fwin)->display; event->xkey.window = fwin; event->xkey.root = (Window) SdlTkX.screen->root; event->xkey.subwindow = None; event->xkey.time = now_ms; event->xkey.x = x; event->xkey.y = y; event->xkey.x_root = x; event->xkey.y_root = y; event->xkey.state = 0; if (state & SDL_BUTTON(1)) { event->xkey.state |= Button1Mask; } if (state & SDL_BUTTON(2)) { event->xkey.state |= Button2Mask; } if (state & SDL_BUTTON(3)) { event->xkey.state |= Button3Mask; } #ifdef TRACE_EVENTS if (sdl_event->type == SDL_TEXTINPUT) { EVLOG(" TEXTINPUT: '%s'", sdl_event->text.text); } else if (sdl_event->type == SDL_TEXTEDITING) { EVLOG("TEXTEDITING: '%s'", sdl_event->edit.text); } else if (sdl_event->type == SDL_KEYDOWN) { EVLOG(" KEYDOWN: CODE=0x%X MOD=0x%X", sdl_event->key.keysym.scancode, sdl_event->key.keysym.mod); } else if (sdl_event->type == SDL_KEYUP) { EVLOG(" KEYUP: CODE=0x%X MOD=0x%X", sdl_event->key.keysym.scancode, sdl_event->key.keysym.mod); } else if (sdl_event->type == SDL_TEXTEDITING) { EVLOG(" KEYUP: CODE=0x%X MOD=0x%X", sdl_event->key.keysym.scancode, sdl_event->key.keysym.mod); } #endif event->xkey.keycode = -1; if (sdl_event->type != SDL_TEXTINPUT && sdl_event->type != SDL_TEXTEDITING) { int scancode = sdl_event->key.keysym.scancode; if (sdl_event->key.keysym.mod & KMOD_LALT) { event->xkey.state |= Mod1Mask; } if (sdl_event->key.keysym.mod & KMOD_RALT) { event->xkey.state |= Mod4Mask; } if (sdl_event->key.keysym.mod & KMOD_CAPS) { event->xkey.state |= LockMask; } if (sdl_event->key.keysym.mod & KMOD_CTRL) { event->xkey.state |= ControlMask; } if (sdl_event->key.keysym.mod & KMOD_NUM) { event->xkey.state |= Mod2Mask; } if (sdl_event->key.keysym.mod & KMOD_SHIFT) { event->xkey.state |= ShiftMask; } /* SDL_SCANCODE_xxx */ event->xkey.keycode = scancode; } event->xkey.same_screen = True; event->xkey.nbytes = 0; if (sdl_event->type == SDL_TEXTINPUT) { int len = strlen(sdl_event->text.text); if (len == 0) { return 0; } if (!ProcessTextInput(event, 0, sdl_event->text.text, len)) { return 0; } } else if (sdl_event->type == SDL_TEXTEDITING) { /* TODO: what can we do here? */ return 0; } else if ((SDL_PeepEvents(&txt_sdl_event, 1, SDL_PEEKEVENT, SDL_TEXTINPUT, SDL_TEXTINPUT) == 1) && (SDL_PeepEvents(&txt_sdl_event, 1, SDL_GETEVENT, SDL_TEXTINPUT, SDL_TEXTINPUT) == 1)) { int len = strlen(txt_sdl_event.text.text); if ((len <= 0) || !ProcessTextInput(event, sdl_event->key.keysym.mod, txt_sdl_event.text.text, len)) { goto doNormalKeyEvent; } } else if ((sdl_event->type == SDL_KEYDOWN) || (sdl_event->type == SDL_KEYUP)) { doNormalKeyEvent: /* Keypad mapping */ if ((event->xkey.keycode >= SDL_SCANCODE_KP_0) && (event->xkey.keycode <= SDL_SCANCODE_KP_9) && (sdl_event->key.keysym.mod & KMOD_NUM)) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '0' + (event->xkey.keycode - SDL_SCANCODE_KP_0); } else if (event->xkey.keycode == SDL_SCANCODE_KP_DIVIDE) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '/'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_MULTIPLY) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '*'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_MINUS) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '-'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_PLUS) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '+'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_ENTER) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '\r'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_PERIOD) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '.'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_COMMA) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = ','; } else if (event->xkey.keycode == SDL_SCANCODE_KP_EQUALS) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '='; } else if (event->xkey.keycode == SDL_SCANCODE_KP_LEFTPAREN) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '('; } else if (event->xkey.keycode == SDL_SCANCODE_KP_RIGHTPAREN) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = ')'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_LEFTBRACE) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '{'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_RIGHTBRACE) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '}'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_VERTICALBAR) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '|'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_TAB) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '\t'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_SPACE) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = ' '; } else if (event->xkey.keycode == SDL_SCANCODE_KP_EXCLAM) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '!'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_AT) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '@'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_HASH) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '#'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_COLON) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = ':'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_AMPERSAND) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '&'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_LESS) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '<'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_GREATER) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '>'; } else if (event->xkey.keycode == SDL_SCANCODE_KP_PERCENT) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = '%'; /* normal mapping */ } else if (event->xkey.keycode == SDL_SCANCODE_SPACE) { event->xkey.nbytes = 1; event->xkey.trans_chars[0] = ' '; } else { MkTransChars(&event->xkey); } } break; } case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: case SDL_MOUSEMOTION: { _Window *_w; int xstate = 0, othergrab = 0; Tk_Window tkwin; SDL_Keymod mod; #ifdef ANDROID if ((info->enabled & (TRANSLATE_RMB | TRANSLATE_PTZ | TRANSLATE_ZOOM)) && (info->fingerBits > 1)) { return 0; } #endif switch (sdl_event->type) { case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONDOWN: x = sdl_event->button.x; y = sdl_event->button.y; state = SDL_BUTTON(sdl_event->button.button); break; case SDL_MOUSEMOTION: { int dx, dy; fix_sdl_event = *sdl_event; sdl_event = &fix_sdl_event; x = sdl_event->motion.x; y = sdl_event->motion.y; dx = sdl_event->motion.xrel; dy = sdl_event->motion.yrel; if (x <= 0 && dx < 0) { dx = 0; } else if (x >= SdlTkX.sdlsurf->w - 1 && dx > 0) { dx = 0; } if (y <= 0 && dy < 0) { dy = 0; } else if (y >= SdlTkX.sdlsurf->h - 1 && dy > 0) { dy = 0; } sdl_event->motion.xrel = (int) (dx / SdlTkX.scale); sdl_event->motion.yrel = (int) (dy / SdlTkX.scale); state = sdl_event->motion.state; #ifdef ANDROID if ((sdl_event->button.which == SDL_TOUCH_MOUSEID) && (sdl_event->button.button == SDL_BUTTON_LEFT)) { TranslateStop(); } #else if (state == 0) { int dwx = x, dwy = y; TranslatePointer(0, &dwx, &dwy); if (dwx < SdlTkX.viewport.x + 10) { dwx = -8; } else if (dwx > SdlTkX.viewport.w + SdlTkX.viewport.x - 10) { dwx = 8; } else { dwx = 0; } if (dwy < SdlTkX.viewport.y + 10) { dwy = -8; } else if (dwy > SdlTkX.viewport.h + SdlTkX.viewport.y - 10) { dwy = 8; } else { dwy = 0; } if (dwx || dwy) { SdlTkPanInt(dwx, dwy); } } #endif break; } } /* now in X coords */ TranslatePointer(0, &x, &y); _w = SdlTkPointToWindow((_Window *) SdlTkX.screen->root, x, y, True, True); SdlTkX.mouse_window = _w; /* * Click in background window to raise it, * unless a Tk grab is on. */ if (!IS_ROOT(_w) && SdlTkGrabCheck(_w, &othergrab) && (sdl_event->type == SDL_MOUSEBUTTONDOWN) && (sdl_event->button.which == SDL_TOUCH_MOUSEID || sdl_event->button.button == SDL_BUTTON_LEFT)) { SdlTkBringToFrontIfNeeded(_w); if (SdlTkX.keyboard_window == NULL) { SdlTkSetInputFocus(SdlTkX.display, (Window) SdlTkWrapperForWindow(_w), RevertToParent, CurrentTime); /* Frames need redrawing if the focus changed */ SdlTkScreenChanged(); } } /* * Possible event in decorative frame (button, drag, resize). * If a menu is showing, give Tk the click so the * menu will go away. I would still like to drag. */ if (SdlTkDecFrameEvent(_w, sdl_event, x, y)) { /* Hide the event from Tk. */ #ifdef ANDROID TranslateStop(); #endif return 0; } if (othergrab) { return 0; } /* NULL for root and decorative frames */ tkwin = (Tk_Window) _w->tkwin; SdlTkX.cursor_change = 1; if (tkwin == NULL && _w->dec != NULL) { SdlTkX.cursor_change = 0; _w = _w->child; tkwin = (Tk_Window) _w->tkwin; } if (SdlTkX.capture_window != NULL) { if (_w->display != SdlTkX.capture_window->display) { tkwin = (Tk_Window) SdlTkX.capture_window; _w = (_Window *) Tk_WindowId(tkwin); } } if (SdlTkX.keyboard_window != NULL) { if (_w->display != SdlTkX.keyboard_window->display) { tkwin = (Tk_Window) SdlTkX.keyboard_window->tkwin; _w = SdlTkX.keyboard_window; } } if (state & SDL_BUTTON(1)) { xstate |= Button1Mask; } if (state & SDL_BUTTON(2)) { xstate |= Button2Mask; } if (state & SDL_BUTTON(3)) { xstate |= Button3Mask; } mod = SDL_GetModState(); if (mod & KMOD_LALT) { xstate |= Mod1Mask; } if (mod & KMOD_RALT) { xstate |= Mod4Mask; } if (mod & KMOD_CAPS) { xstate |= LockMask; } if (mod & KMOD_CTRL) { xstate |= ControlMask; } if (mod & KMOD_NUM) { xstate |= Mod2Mask; } if (mod & KMOD_SHIFT) { xstate |= ShiftMask; } if (sdl_event->type == SDL_MOUSEBUTTONUP) { if (state & SDL_BUTTON(1)) { xstate &= ~Button1Mask; } if (state & SDL_BUTTON(2)) { xstate &= ~Button2Mask; } if (state & SDL_BUTTON(3)) { xstate &= ~Button3Mask; } } if (sdl_event->type == SDL_MOUSEBUTTONDOWN) { int bstate = xstate; if (state & SDL_BUTTON(1)) { bstate &= ~Button1Mask; } if (state & SDL_BUTTON(2)) { bstate &= ~Button2Mask; } if (state & SDL_BUTTON(3)) { bstate &= ~Button3Mask; } } SdlTkX.mouse_x = x; SdlTkX.mouse_y = y; if (tkwin != NULL) { SendPointerUpdate(tkwin, x, y, xstate); } return 0; } case SDL_MOUSEWHEEL: { int xstate = 0; Tk_Window tkwin = NULL; #ifndef ANDROID SDL_Keymod mod; mod = SDL_GetModState(); if (mod & KMOD_LCTRL) { float dir = 0; if (sdl_event->wheel.y > 0) { dir = 0.96; } else if (sdl_event->wheel.y < 0) { dir = 1.0 / 0.96; } if (dir) { SdlTkZoomInt(SdlTkX.mouse_x, SdlTkX.mouse_y, dir); } return 0; } #endif if (SdlTkX.mouse_window != NULL) { tkwin = (Tk_Window) SdlTkX.mouse_window->tkwin; } if (SdlTkX.capture_window != NULL) { if (tkwin == NULL || Tk_Display(tkwin) != SdlTkX.capture_window->display) { tkwin = (Tk_Window) SdlTkX.capture_window; } } if (SdlTkX.keyboard_window != NULL) { if (tkwin == NULL || Tk_Display(tkwin) != SdlTkX.keyboard_window->display) { tkwin = (Tk_Window) SdlTkX.keyboard_window->tkwin; } } if (sdl_event->wheel.y < 0) { xstate |= Button5Mask; } else if (sdl_event->wheel.y > 0) { xstate |= Button4Mask; } if (tkwin && xstate) { SendPointerUpdate(tkwin, SdlTkX.mouse_x, SdlTkX.mouse_y, xstate); SendPointerUpdate(tkwin, SdlTkX.mouse_x, SdlTkX.mouse_y, 0); } return 0; } case SDL_QUIT: if (SdlTkDecFrameEvent((_Window *) SdlTkX.screen->root, sdl_event, 0, 0)) { return 0; } SdlTkUnlock(NULL); Tcl_Exit(0); break; case SDL_APP_LOWMEMORY: evname = "LowMemory"; goto doAppEvent; case SDL_APP_TERMINATING: evname = "Terminating"; goto doAppEvent; case SDL_APP_WILLENTERBACKGROUND: evname = "WillEnterBackground"; goto doAppEvent; case SDL_APP_DIDENTERBACKGROUND: SdlTkX.in_background = 1; evname = "DidEnterBackground"; goto doAppEvent; case SDL_APP_WILLENTERFOREGROUND: evname = "WillEnterForeground"; goto doAppEvent; case SDL_APP_DIDENTERFOREGROUND: evname = "DidEnterForeground"; #ifdef ANDROID ConfigGLWindows(SdlTkX.screen->root); #endif doAppEvent: { int nsent = 0; #ifdef ANDROID TranslateStop(); #endif EVLOG("EV=APPEVENT '%s'", evname); memset(event, 0, sizeof (*event)); event->xany.type = VirtualEvent; event->xany.send_event = False; event->xany.window = (Window) SdlTkX.screen->root; event->xbutton.root = (Window) SdlTkX.screen->root; event->xany.display = SdlTkX.display; ((XVirtualEvent *) event)->name = (Tk_Uid) evname; event->xany.serial = SdlTkX.display->request; /* only TK_APP_TOP_LEVEL windows will get this */ event->xany.window = (Window) SendAppEvent(event, &nsent, ((_Window *) SdlTkX.screen->root)->child); return nsent > 0; } case SDL_USEREVENT: { int nsent = 0; if (sdl_event->user.data1 != NULL) { evname = sdl_event->user.data1; } if (evname != NULL) { EVLOG("EV=USEREVENT '%s'", evname); memset(event, 0, sizeof (*event)); event->xany.type = VirtualEvent; event->xany.send_event = False; event->xany.window = (Window) SdlTkX.screen->root; event->xbutton.root = (Window) SdlTkX.screen->root; event->xany.display = SdlTkX.display; ((XVirtualEvent *) event)->name = (Tk_Uid) evname; event->xany.serial = SdlTkX.display->request; event->xbutton.x = event->xbutton.y = sdl_event->user.code; event->xbutton.state = (long) sdl_event->user.data2; /* only TK_APP_TOP_LEVEL windows will get this */ event->xany.window = (Window) SendAppEvent(event, &nsent, ((_Window *) SdlTkX.screen->root)->child); } return nsent > 0; } case SDL_FINGERDOWN: evname = "FingerDown"; goto doFingerEvent; case SDL_FINGERUP: evname = "FingerUp"; goto doFingerEvent; case SDL_FINGERMOTION: evname = "FingerMotion"; doFingerEvent: { _Window *_w; Tk_Window tkwin = NULL; FingerToScreen(sdl_event, &x, &y); _w = SdlTkPointToWindow((_Window *) SdlTkX.screen->root, x, y, True, True); if (_w != NULL) { tkwin = (Tk_Window) _w->tkwin; } if (tkwin == NULL) { tkwin = (Tk_Window) SdlTkX.capture_window; if (tkwin != NULL) { _w = (_Window *) Tk_WindowId(tkwin); } } if (SdlTkX.capture_window != NULL) { if (_w == NULL || _w->display != SdlTkX.capture_window->display) { tkwin = (Tk_Window) SdlTkX.capture_window; _w = (_Window *) Tk_WindowId(tkwin); } } if (SdlTkX.keyboard_window != NULL) { if (_w == NULL || _w->display != SdlTkX.keyboard_window->display) { tkwin = (Tk_Window) SdlTkX.keyboard_window->tkwin; _w = SdlTkX.keyboard_window; } } if (tkwin != NULL) { int pressure; #ifdef ANDROID if ((info->enabled & TRANSLATE_FBTNS) && ((sdl_event->type == SDL_FINGERDOWN) || (sdl_event->type == SDL_FINGERUP))) { int wx, wy; /* make ButtonPress/ButtonRelease for buttons 10-19 */ SdlTkRootCoords(_w, &wx, &wy); memset(event, 0, sizeof (*event)); event->xbutton.type = (sdl_event->type == SDL_FINGERUP) ? ButtonRelease : ButtonPress; event->xbutton.serial = Tk_Display(tkwin)->request; event->xbutton.send_event = False; event->xbutton.display = Tk_Display(tkwin); event->xbutton.window = Tk_WindowId(tkwin); event->xbutton.root = (Window) SdlTkX.screen->root; event->xbutton.time = now_ms; event->xbutton.x = x - wx; event->xbutton.y = y - wy; event->xbutton.x_root = x; event->xbutton.y_root = y; event->xbutton.state = 0; event->xbutton.button = sdl_event->tfinger.fingerId + 10; event->xbutton.same_screen = True; SdlTkQueueEvent(event); } if (info->enabled & TRANSLATE_FINGER) { TranslateFinger(sdl_event, &tmp_event); sdl_event = &tmp_event; } #endif memset(event, 0, sizeof (*event)); event->xany.type = VirtualEvent; event->xany.serial = Tk_Display(tkwin)->request; event->xany.send_event = False; event->xany.window = Tk_WindowId(tkwin); event->xbutton.root = (Window) SdlTkX.screen->root; event->xany.display = Tk_Display(tkwin); event->xbutton.x = sdl_event->tfinger.x * 10000; if (event->xbutton.x >= 10000) { event->xbutton.x = 9999; } else if (event->xbutton.x < 0) { event->xbutton.x = 0; } event->xbutton.y = sdl_event->tfinger.y * 10000; if (event->xbutton.y >= 10000) { event->xbutton.y = 9999; } else if (event->xbutton.y < 0) { event->xbutton.y = 0; } event->xbutton.x_root = sdl_event->tfinger.dx * 10000; if (event->xbutton.x_root >= 10000) { event->xbutton.x_root = 9999; } else if (event->xbutton.x_root < 0) { event->xbutton.x_root = 0; } event->xbutton.y_root = sdl_event->tfinger.dy * 10000; if (event->xbutton.y_root >= 10000) { event->xbutton.y_root = 9999; } else if (event->xbutton.y_root < 0) { event->xbutton.y_root = 0; } pressure = sdl_event->tfinger.pressure * 10000; if (pressure >= 10000) { pressure = 9999; } else if (pressure < 0) { pressure = 0; } event->xbutton.time = pressure; event->xbutton.state = sdl_event->tfinger.fingerId + 1; ((XVirtualEvent *) event)->name = (Tk_Uid) evname; return 1; } return 0; } case SDL_CLIPBOARDUPDATE: SdlTkSetSelectionOwner(SdlTkX.display, None, None, now_ms); return 0; case SDL_JOYDEVICEADDED: case SDL_JOYDEVICEREMOVED: { int nsent = 0; memset(event, 0, sizeof (*event)); event->xany.type = VirtualEvent; event->xany.send_event = False; event->xany.window = (Window) SdlTkX.screen->root; event->xbutton.root = (Window) SdlTkX.screen->root; event->xany.display = SdlTkX.display; event->xany.serial = SdlTkX.display->request; event->xbutton.state = sdl_event->jdevice.which; event->xbutton.x_root = event->xbutton.y_root = sdl_event->jdevice.which; if (sdl_event->type == SDL_JOYDEVICEADDED) { SDL_Joystick *stick; long which; int new; Tcl_HashEntry *hPtr; stick = SDL_JoystickOpen(sdl_event->jdevice.which); if (stick == NULL) { return 0; } event->xbutton.state = SDL_JoystickInstanceID(stick); event->xbutton.x_root = event->xbutton.y_root = event->xbutton.state; #ifdef ANDROID if (strcmp(SDL_JoystickName(stick), "Android Accelerometer") == 0) { SdlTkX.accel_id = event->xbutton.state; } #endif which = event->xbutton.state; hPtr = Tcl_CreateHashEntry(&SdlTkX.joystick_table, (ClientData) which, &new); if (!new) { SDL_JoystickClose(stick); return 0; } Tcl_SetHashValue(hPtr, (char *) stick); ((XVirtualEvent *) event)->name = (Tk_Uid) "JoystickAdded"; } else { long which = sdl_event->jdevice.which; Tcl_HashEntry *hPtr; hPtr = Tcl_FindHashEntry(&SdlTkX.joystick_table, (ClientData) which); if (hPtr == NULL) { return 0; } SDL_JoystickClose((SDL_Joystick *) Tcl_GetHashValue(hPtr)); Tcl_DeleteHashEntry(hPtr); ((XVirtualEvent *) event)->name = (Tk_Uid) "JoystickRemoved"; } /* only TK_APP_TOP_LEVEL windows will get this */ event->xany.window = (Window) SendAppEvent(event, &nsent, ((_Window *) SdlTkX.screen->root)->child); return nsent > 0; } case SDL_JOYAXISMOTION: { int nsent = 0; #ifdef ANDROID int delta; if (sdl_event->jaxis.which == SdlTkX.accel_id) { delta = AddToAccelRing(now_ms, sdl_event->jaxis.value, sdl_event->jaxis.axis); if (!SdlTkX.accel_enabled) { return 0; } } #endif memset(event, 0, sizeof (*event)); event->xany.type = VirtualEvent; event->xany.send_event = False; event->xany.window = (Window) SdlTkX.screen->root; event->xbutton.root = (Window) SdlTkX.screen->root; event->xany.display = SdlTkX.display; event->xany.serial = SdlTkX.display->request; event->xbutton.time = now_ms; event->xbutton.x = event->xbutton.y = sdl_event->jaxis.value; event->xbutton.state = sdl_event->jaxis.axis + 1; #ifdef ANDROID if (sdl_event->jaxis.which == SdlTkX.accel_id) { ((XVirtualEvent *) event)->name = (Tk_Uid) "Accelerometer"; event->xbutton.x_root = event->xbutton.y_root = delta; } else #endif { event->xbutton.x_root = event->xbutton.y_root = sdl_event->jaxis.which; ((XVirtualEvent *) event)->name = (Tk_Uid) "JoystickMotion"; } /* only TK_APP_TOP_LEVEL windows will get this */ event->xany.window = (Window) SendAppEvent(event, &nsent, ((_Window *) SdlTkX.screen->root)->child); return nsent > 0; } case SDL_JOYBALLMOTION: { int nsent = 0; memset(event, 0, sizeof (*event)); event->xany.type = VirtualEvent; event->xany.send_event = False; event->xany.window = (Window) SdlTkX.screen->root; event->xany.display = SdlTkX.display; event->xany.serial = SdlTkX.display->request; event->xbutton.x = sdl_event->jball.xrel; event->xbutton.y = sdl_event->jball.yrel; event->xbutton.state = sdl_event->jball.ball + 1; event->xbutton.x_root = event->xbutton.y_root = sdl_event->jball.which; ((XVirtualEvent *) event)->name = (Tk_Uid) "TrackballMotion"; /* only TK_APP_TOP_LEVEL windows will get this */ event->xany.window = (Window) SendAppEvent(event, &nsent, ((_Window *) SdlTkX.screen->root)->child); return nsent > 0; } case SDL_JOYHATMOTION: { int nsent = 0; memset(event, 0, sizeof (*event)); event->xany.type = VirtualEvent; event->xany.send_event = False; event->xany.window = (Window) SdlTkX.screen->root; event->xany.display = SdlTkX.display; event->xany.serial = SdlTkX.display->request; event->xbutton.x = event->xbutton.y = sdl_event->jhat.value; event->xbutton.state = sdl_event->jhat.hat + 1; event->xbutton.x_root = event->xbutton.y_root = sdl_event->jhat.which; ((XVirtualEvent *) event)->name = (Tk_Uid) "HatPosition"; /* only TK_APP_TOP_LEVEL windows will get this */ event->xany.window = (Window) SendAppEvent(event, &nsent, ((_Window *) SdlTkX.screen->root)->child); return nsent > 0; } case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: { int nsent = 0; memset(event, 0, sizeof (*event)); event->xany.type = VirtualEvent; event->xany.send_event = False; event->xany.window = (Window) SdlTkX.screen->root; event->xany.display = SdlTkX.display; event->xany.serial = SdlTkX.display->request; event->xbutton.state = sdl_event->jbutton.button + 1; event->xbutton.x_root = event->xbutton.y_root = sdl_event->jbutton.which; ((XVirtualEvent *) event)->name = (Tk_Uid) ((sdl_event->type == SDL_JOYBUTTONUP) ? "JoystickButtonUp" : "JoystickButtonDown"); /* only TK_APP_TOP_LEVEL windows will get this */ event->xany.window = (Window) SendAppEvent(event, &nsent, ((_Window *) SdlTkX.screen->root)->child); return nsent > 0; } case SDL_WINDOWEVENT: { int width, height, oldw, oldh, ow, oh; SDL_PixelFormat *pfmt; SDL_Surface *newsurf = NULL; SDL_Texture *newtex = NULL; _Window *_w; #ifndef ANDROID int tfmt = SDL_PIXELFORMAT_RGB888; #endif switch (sdl_event->window.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: EVLOG("EV=WINDOWEVENT_SIZE_CHANGED"); width = sdl_event->window.data1; height = sdl_event->window.data2; if (SdlTkX.root_w) { SDL_GetWindowSize(SdlTkX.sdlscreen, &oldw, &oldh); } else { oldw = SdlTkX.screen->width; oldh = SdlTkX.screen->height; } if ((width == oldw) && (height == oldh)) { return 0; } EVLOG(" width=%d height=%d", width, height); if (SdlTkX.root_w) { float aspReal, aspRoot; aspReal = (float) width / height; aspRoot = (float) SdlTkX.root_w / SdlTkX.root_h; SdlTkX.scale_min = 1.0f; if (SDL_fabs(aspRoot - aspReal) < 0.0001) { if (SdlTkX.root_w > width) { SdlTkX.scale_min = (float) width / SdlTkX.root_w; } } else if (aspRoot > aspReal) { if (SdlTkX.root_w > width) { SdlTkX.scale_min = (float) width / SdlTkX.root_w; } } else { if (SdlTkX.root_h > height) { SdlTkX.scale_min = (float) height / SdlTkX.root_h; } } SdlTkPanZoom(1, SdlTkX.viewport.x, SdlTkX.viewport.y, SdlTkX.viewport.w, SdlTkX.viewport.h); return 0; } pfmt = SdlTkX.sdlsurf->format; newsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, pfmt->BitsPerPixel, pfmt->Rmask, pfmt->Gmask, pfmt->Bmask, pfmt->Amask); #ifndef ANDROID if (pfmt->BitsPerPixel == 15) { tfmt = SDL_PIXELFORMAT_RGB555; } else if (pfmt->BitsPerPixel == 16) { tfmt = SDL_PIXELFORMAT_RGB565; } else if (pfmt->BitsPerPixel == 24) { if (pfmt->BytesPerPixel == 3) { tfmt = SDL_PIXELFORMAT_RGB24; } } #endif newtex = SDL_CreateTexture(SdlTkX.sdlrend, #ifdef ANDROID SDL_PIXELFORMAT_RGB888, #else tfmt, #endif SDL_TEXTUREACCESS_STREAMING, width, height); #ifdef ANDROID if (newsurf != NULL && newtex != NULL) { SDL_GL_SwapWindow(SdlTkX.sdlscreen); } #endif if (newsurf != NULL && newtex != NULL) { #ifdef ANDROID int xdpi, ydpi; #endif SDL_Rect sr; Uint32 pixel; _Window *child; Display *dpy; SDL_BlitSurface(SdlTkX.sdlsurf, NULL, newsurf, NULL); SDL_FreeSurface(SdlTkX.sdlsurf); SdlTkX.sdlsurf = newsurf; SDL_DestroyTexture(SdlTkX.sdltex); SdlTkX.sdltex = newtex; SdlTkX.screen->width = width; SdlTkX.screen->height = height; #ifdef ANDROID xdpi = ydpi = 0; SDL_GetWindowDPI(SdlTkX.sdlscreen, &xdpi, &ydpi); if (xdpi && ydpi) { SdlTkX.screen->mwidth = (254 * width) / xdpi; SdlTkX.screen->mwidth /= 10; SdlTkX.screen->mheight = (254 * height) / ydpi; SdlTkX.screen->mheight /= 10; } else { SdlTkX.screen->mwidth = (width * 254 + 360) / 1440; SdlTkX.screen->mheight = (height * 254 + 360) / 1440; } #else SdlTkX.screen->mwidth = (width * 254 + 360) / 720; SdlTkX.screen->mheight = (height * 254 + 360) / 720; #endif dpy = SdlTkX.display->next_display; while (dpy != NULL) { dpy->screens[0].width = SdlTkX.screen->width; dpy->screens[0].height = SdlTkX.screen->height; if (dpy->screens[0].moverride) { if (((width > oldw) && (height < oldh)) || ((width < oldw) && (height > oldh))) { int swp = dpy->screens[0].mwidth; dpy->screens[0].mwidth = dpy->screens[0].mheight; dpy->screens[0].mheight = swp; } } else { dpy->screens[0].mwidth = SdlTkX.screen->mwidth; dpy->screens[0].mheight = SdlTkX.screen->mheight; } dpy = dpy->next_display; } _w = (_Window *) SdlTkX.screen->root; _w->atts.width = _w->parentWidth = width; _w->atts.height = _w->parentHeight = height; pixel = SDL_MapRGB(SdlTkX.sdlsurf->format, #ifdef ANDROID 0x00, 0x00, 0x00 #else 0x00, 0x4E, 0x78 #endif ); if (width > oldw) { sr.x = oldw; sr.w = width - oldw; sr.y = 0; sr.h = height; SDL_FillRect(SdlTkX.sdlsurf, &sr, pixel); } if (height > oldh) { sr.y = oldh; sr.h = height - oldh; sr.x = 0; sr.w = width; SDL_FillRect(SdlTkX.sdlsurf, &sr, pixel); } if (width > oldw || height > oldh) { SdlTkVisRgnChanged(_w, VRC_CHANGED, 0, 0); } child = _w->child; while (child != NULL) { if (child->fullscreen) { int xx, yy, ww, hh; _Window *_ww = child; xx = 0; yy = 0; ww = width; hh = height; if (child->dec != NULL) { xx -= SdlTkX.dec_frame_width; yy -= SdlTkX.dec_title_height; _ww = child->child; } child->fullscreen = 0; _ww->fullscreen = 0; SdlTkMoveResizeWindow(SdlTkX.display, (Window) _ww, xx, yy, ww, hh); _ww->fullscreen = 1; child->fullscreen = 1; } child = child->next; } if (SdlTkX.draw_later & SDLTKX_SCALED) { int vw, vh, vx, vy; vw = (int) (width / SdlTkX.scale); vh = (int) (height / SdlTkX.scale); vx = SdlTkX.viewport.x + (SdlTkX.viewport.w - vw) / 2; vy = SdlTkX.viewport.y + (SdlTkX.viewport.h - vh) / 2; if (width - (vw + vx) < 0) { vx = width - vw; } if (vx < 0) { vx = 0; } if (height - (vh + vy) < 0) { vy = height - vh; } if (vy < 0) { vy = 0; } if (vw > width) { vw = width; } if (vh > height) { vh = height; } SdlTkX.viewport.x = vx; SdlTkX.viewport.y = vy; SdlTkX.viewport.w = vw; SdlTkX.viewport.h = vh; ow = (int) SDL_ceil(vw * SdlTkX.scale); oh = (int) SDL_ceil(vh * SdlTkX.scale); if (ow < width || oh < height) { SdlTkX.outrect = &SdlTkX.outrect0; SdlTkX.outrect->x = (width - ow) / 2; SdlTkX.outrect->y = (height - oh) / 2; SdlTkX.outrect->w = ow; SdlTkX.outrect->h = oh; } else { SdlTkX.outrect = NULL; } } else { SdlTkX.viewport.w = width; SdlTkX.viewport.h = height; SdlTkX.outrect = NULL; } SdlTkSendViewportUpdate(); SDL_SetRenderTarget(SdlTkX.sdlrend, NULL); SDL_RenderSetViewport(SdlTkX.sdlrend, NULL); } else { if (newsurf != NULL) { SDL_FreeSurface(newsurf); } if (newtex != NULL) { SDL_DestroyTexture(newtex); } } SdlTkX.draw_later |= SDLTKX_RENDCLR | SDLTKX_PRESENT; goto fullRefresh; case SDL_WINDOWEVENT_FOCUS_GAINED: EVLOG("EV=WINDOWEVENT_FOCUS_GAINED"); if (!SdlTkX.sdlfocus) { SdlTkX.sdlfocus = 1; if (SdlTkX.focus_window_old != None) { SdlTkSetInputFocus(SdlTkX.display, SdlTkX.focus_window_old, RevertToParent, CurrentTime); goto refresh; } } return 0; case SDL_WINDOWEVENT_FOCUS_LOST: EVLOG("EV=WINDOWEVENT_FOCUS_LOST"); if (SdlTkX.sdlfocus) { SdlTkX.sdlfocus = 0; SdlTkX.focus_window_old = SdlTkX.focus_window; if (SdlTkX.focus_window != None) { SdlTkSetInputFocus(SdlTkX.display, None, RevertToNone, CurrentTime); goto refresh; } } return 0; case SDL_WINDOWEVENT_HIDDEN: EVLOG("EV=WINDOWEVENT_HIDDEN"); return 0; case SDL_WINDOWEVENT_MOVED: EVLOG("EV=WINDOWEVENT_MOVED"); return 0; case SDL_WINDOWEVENT_RESIZED: EVLOG("EV=WINDOWEVENT_RESIZED"); return 0; case SDL_WINDOWEVENT_MINIMIZED: EVLOG("EV=WINDOWEVENT_MINIMIZED"); return 0; case SDL_WINDOWEVENT_MAXIMIZED: EVLOG("EV=WINDOWEVENT_MAXIMIZED"); return 0; case SDL_WINDOWEVENT_ENTER: EVLOG("EV=WINDOWEVENT_ENTER"); return 0; case SDL_WINDOWEVENT_LEAVE: EVLOG("EV=WINDOWEVENT_LEAVE"); return 0; case SDL_WINDOWEVENT_CLOSE: EVLOG("EV=WINDOWEVENT_CLOSE"); return 0; case SDL_WINDOWEVENT_RESTORED: EVLOG("EV=WINDOWEVENT_RESTORED"); goto fullRefresh; case SDL_WINDOWEVENT_SHOWN: EVLOG("EV=WINDOWEVENT_SHOWN"); goto fullRefresh; case SDL_WINDOWEVENT_EXPOSED: EVLOG("EV=WINDOWEVENT_EXPOSED"); fullRefresh: SdlTkX.in_background = 0; SdlTkX.draw_later |= SDLTKX_DRAW | SDLTKX_DRAWALL; #ifdef ANDROID SdlTkScreenRefresh(); #endif return 0; refresh: SdlTkScreenChanged(); return 0; } return 0; default: return 0; } } return 1; } /* *---------------------------------------------------------------------- * * SdlTkCalculateVisibleRegion -- * * This procedure calculates the visible region for a window. * * NOTE: The caller is responsible for freeing the old visRgn * and visRgnInParent. * * NOTE: The parent's visRgnInParent must be correct before * this procedure may be called. * * Results: * None. * * Side effects: * The visRgn and visRgnInParent members of the * given window are changed. * *---------------------------------------------------------------------- */ void SdlTkCalculateVisibleRegion(_Window *_w) { XRectangle rect; _Window *ancestor, *child, *parent = _w->parent; /* Caller must free old ones */ _w->visRgn = SdlTkRgnPoolGet(); _w->visRgnInParent = SdlTkRgnPoolGet(); /* An unmapped window has an empty visible region */ if (_w->atts.map_state == IsUnmapped) { return; } /* * If any ancestor is unmapped, this window has an empty * visible region. In X11, a window may be mapped even if an * ancestor isn't. */ if (parent != NULL) { ancestor = parent; while (!IS_ROOT(ancestor)) { if (ancestor->atts.map_state == IsUnmapped) { return; } ancestor = ancestor->parent; } } /* Start with a rectangle as large as ourself in our parent */ rect.x = _w->atts.x; rect.y = _w->atts.y; rect.width = _w->parentWidth; rect.height = _w->parentHeight; XUnionRectWithRegion(&rect, _w->visRgnInParent, _w->visRgnInParent); if (parent != NULL) { /* Intersect with parent's visRgnInParent */ XIntersectRegion(_w->visRgnInParent, parent->visRgnInParent, _w->visRgnInParent); if (XEmptyRegion(_w->visRgnInParent)) { /* Entirely outside of parent's visRgn, we're done. */ return; } /* * Subtract one rectangle for each mapped sibling higher in the * stacking order. */ if (parent->child != _w) { Region rgn2 = SdlTkRgnPoolGet(); child = parent->child; while (child != _w) { if ((child->atts.map_state != IsUnmapped) && (XRectInRegion(_w->visRgnInParent, child->atts.x, child->atts.y, child->parentWidth, child->parentHeight) != RectangleOut)) { rect.x = child->atts.x; rect.y = child->atts.y; rect.width = child->parentWidth; rect.height = child->parentHeight; XUnionRectWithRegion(&rect, rgn2, rgn2); } child = child->next; } XSubtractRegion(_w->visRgnInParent, rgn2, _w->visRgnInParent); SdlTkRgnPoolFree(rgn2); /* A window's siblings may completely obscure it */ if (XEmptyRegion(_w->visRgnInParent)) { if (!PARENT_IS_ROOT(_w)) { return; } } } } /* * Calculation of visRgnInParent is complete. Copy visRgnInParent * into visRgn. */ XUnionRegion(_w->visRgnInParent, _w->visRgn, _w->visRgn); /* * Subtract one rectangle for each mapped child from visRgn (a window * can't draw over its children) */ if (_w->child != NULL) { Region rgn2 = SdlTkRgnPoolGet(); for (child = _w->child; child != NULL; child = child->next) { if (child->atts.map_state != IsUnmapped) { rect.x = _w->atts.x + child->atts.x; rect.y = _w->atts.y + child->atts.y; rect.width = child->parentWidth; rect.height = child->parentHeight; XUnionRectWithRegion(&rect, rgn2, rgn2); } } XSubtractRegion(_w->visRgn, rgn2, _w->visRgn); SdlTkRgnPoolFree(rgn2); } /* Make relative to this window's top-left corner */ XOffsetRegion(_w->visRgn, -_w->atts.x, -_w->atts.y); XOffsetRegion(_w->visRgnInParent, -_w->atts.x, -_w->atts.y); } /* *---------------------------------------------------------------------- * * blitMovedWindow -- * * Helper routine for SdlTkVisRgnChanged(). Copies the visible * pixels of a window from its old location to its new location. * * Results: * None. * * Side effects: * Pixels will be blitted within the drawing surface of the * window's parent. * *---------------------------------------------------------------------- */ static void blitMovedWindow(_Window *_w, int x, int y) { XGCValues fakeGC; TkpClipMask clip; int xOff, yOff; Region parentVisRgn; /* * Hack: SdlTkGfxCopyArea will clip to our parent's visRgn. Need to * clip to our parent's visRgnInParent instead. */ parentVisRgn = _w->parent->visRgn; _w->parent->visRgn = _w->parent->visRgnInParent; /* * This window's visRgnInParent is used as the clip region. Put * it in the parent's coordinates. */ XOffsetRegion(_w->visRgnInParent, _w->atts.x, _w->atts.y); clip.type = TKP_CLIP_REGION; clip.value.region = (TkRegion) _w->visRgnInParent; fakeGC.clip_mask = (Pixmap) &clip; fakeGC.graphics_exposures = False; fakeGC.clip_x_origin = 0; fakeGC.clip_y_origin = 0; /* * What actually needs to be done is copy everything inside the * old visRgnInParent to the new location. */ SdlTkGfxCopyArea((Drawable) _w->parent, (Drawable) _w->parent, &fakeGC, x, y, _w->parentWidth, _w->parentHeight, _w->atts.x, _w->atts.y); XOffsetRegion(_w->visRgnInParent, -_w->atts.x, -_w->atts.y); /* Undo hack */ _w->parent->visRgn = parentVisRgn; /* * Add its visible area to the screen's dirtyRgn. */ SdlTkRootCoords(_w, &xOff, &yOff); XOffsetRegion(_w->visRgnInParent, xOff, yOff); if (SdlTkX.screen_update_region == NULL) { SdlTkX.screen_update_region = SdlTkRgnPoolGet(); } XUnionRegion(_w->visRgnInParent, SdlTkX.screen_update_region, SdlTkX.screen_update_region); XOffsetRegion(_w->visRgnInParent, -xOff, -yOff); } /* *---------------------------------------------------------------------- * * SdlTkVisRgnChanged -- * * This procedure gets called whenever a window is mapped, * unmapped, raised, lowered, moved, or resized. It recalculates * the visible regions for all windows affected by the change. * * Results: * None. * * Side effects: * The visRgn, visRgnInParent and dirtyRgn * members of various windows may be updated. If the window * was moved, then pixels may be copied within its parent's * drawing surface. events may be added to the event * queue. * *---------------------------------------------------------------------- */ static void SdlTkVisRgnChangedInt(_Window *_w, int flags, int x, int y) { int cleared = 0; Region visRgnInParent = NULL; /* * If this is NOT the window that was mapped/unmapped/moved/ * resized/restacked and it is unmapped, don't bother recalculating * visible regions for this window. */ if ((flags & VRC_CHANGED) || (_w->atts.map_state != IsUnmapped)) { Region visRgn; /* * A window obscures part of its parent. * Update parent's visible region before blitting this window. */ if ((flags & VRC_DO_PARENT) && (_w->parent != NULL)) { SdlTkVisRgnChangedInt(_w->parent, VRC_SELF_ONLY, 0, 0); } /* Save the old regions temporarily so changes can be examined */ visRgn = _w->visRgn; visRgnInParent = _w->visRgnInParent; SdlTkCalculateVisibleRegion(_w); /* * If this window moved within its parent, copy all the visible * pixels to the new location. */ if ((flags & VRC_MOVE) && !XEmptyRegion(_w->visRgnInParent)) { blitMovedWindow(_w, x, y); } if (_w->atts.map_state != IsUnmapped) { /* Take away what was visible before */ XSubtractRegion(_w->visRgn, visRgn, visRgn); /* * Generate events for newly-exposed areas. * Only generate events for real Tk windows, not a * decframe or wrapper */ if (_w->tkwin != NULL) { cleared = SdlTkGfxClearRegion((Window) _w, visRgn); if ((cleared && !(flags & VRC_EXPOSE)) || (_w->gl_tex != NULL)) { SdlTkGfxExposeRegion((Window) _w, visRgn); } cleared = 1; } else if (_w->dec != NULL) { if (!XEmptyRegion(visRgn)) { SdlTkDecSetDraw(_w, 1); } cleared = 1; } else if (IS_ROOT(_w)) { /* * Can't erase right away, because it will erase the pixels of * any toplevels which moved (I want to blit those pixels). */ if (SdlTkX.screen_dirty_region == NULL) { SdlTkX.screen_dirty_region = SdlTkRgnPoolGet(); } XUnionRegion(SdlTkX.screen_dirty_region, visRgn, SdlTkX.screen_dirty_region); } } SdlTkRgnPoolFree(visRgn); } if (flags & VRC_EXPOSE) { if (_w->tkwin != NULL) { SdlTkGfxExposeRegion((Window) _w, _w->visRgn); } } if (!(flags & VRC_SELF_ONLY)) { /* * If this is NOT the window that was mapped/unmapped/moved/ * resized/restacked and it is unmapped, don't bother * recalculating visible regions for any descendants. */ if ((flags & (VRC_CHANGED | VRC_EXPOSE)) || (_w->atts.map_state != IsUnmapped)) { /* * If this window's visRgnInParent did not change (such as when * moving a toplevel), don't recalculate the visible * regions of any descendants. */ if ((flags & VRC_EXPOSE) || cleared || !XEqualRegion(_w->visRgnInParent, visRgnInParent)) { /* * We only need to do our first child, because it will do * its next sibling etc. */ if (_w->child != NULL) { SdlTkVisRgnChangedInt(_w->child, cleared ? VRC_EXPOSE : 0, 0, 0); } } } if (visRgnInParent != NULL) { SdlTkRgnPoolFree(visRgnInParent); visRgnInParent = NULL; } /* A window may obscure siblings lower in the stacking order */ if (!(flags & VRC_DO_SIBLINGS)) { while (_w->next != NULL) { SdlTkVisRgnChangedInt(_w->next, VRC_DO_SIBLINGS | (flags & VRC_EXPOSE), 0, 0); _w = _w->next; } } } if (visRgnInParent != NULL) { SdlTkRgnPoolFree(visRgnInParent); visRgnInParent = NULL; } } void SdlTkVisRgnChanged(_Window *_w, int flags, int x, int y) { SdlTkVisRgnChangedInt(_w, flags, x, y); /* If areas of the root window were exposed, paint them now */ if (SdlTkX.screen_dirty_region && !XEmptyRegion(SdlTkX.screen_dirty_region)) { Uint32 pixel = SDL_MapRGB(SdlTkX.sdlsurf->format, #ifdef ANDROID 0x00, 0x00, 0x00 #else 0x00, 0x4E, 0x78 #endif ); SdlTkGfxFillRegion(SdlTkX.screen->root, SdlTkX.screen_dirty_region, pixel); if (SdlTkX.screen_update_region == NULL) { SdlTkX.screen_update_region = SdlTkRgnPoolGet(); } XUnionRegion(SdlTkX.screen_dirty_region, SdlTkX.screen_update_region, SdlTkX.screen_update_region); XSetEmptyRegion(SdlTkX.screen_dirty_region); } } Region SdlTkGetVisibleRegion(_Window *_w) { if (_w->visRgn == NULL) { _w->visRgn = SdlTkRgnPoolGet(); } return _w->visRgn; } /* *---------------------------------------------------------------------- * * SdlTkGenerateConfigureNotify -- * * Generate a event for a window. * * Results: * None. * * Side effects: * A window event is added to the event queue. * *---------------------------------------------------------------------- */ void SdlTkGenerateConfigureNotify(Window w) { _Window *_w = (_Window *) w; XEvent event; memset(&event, 0, sizeof (event)); event.type = ConfigureNotify; event.xconfigure.serial = _w->display->request; event.xconfigure.send_event = False; event.xconfigure.display = _w->display; event.xconfigure.event = w; event.xconfigure.window = w; event.xconfigure.border_width = _w->atts.border_width; event.xconfigure.override_redirect = _w->atts.override_redirect; event.xconfigure.x = _w->atts.x; event.xconfigure.y = _w->atts.y; event.xconfigure.width = _w->atts.width; event.xconfigure.height = _w->atts.height; event.xconfigure.above = None; SdlTkQueueEvent(&event); } /* *---------------------------------------------------------------------- * * SdlTkGenerateExpose -- * * Generate an event for part of a window. * * Results: * None. * * Side effects: * A window event is added to the event queue. * *---------------------------------------------------------------------- */ void SdlTkGenerateExpose(Window w, int x, int y, int width, int height, int count) { _Window *_w = (_Window *) w; XEvent event; memset(&event, 0, sizeof (event)); event.type = Expose; event.xexpose.serial = _w->display->request; event.xexpose.send_event = False; event.xexpose.display = _w->display; event.xexpose.window = w; event.xexpose.x = x; event.xexpose.y = y; event.xexpose.width = width; event.xexpose.height = height; event.xexpose.count = count; SdlTkQueueEvent(&event); } /* *---------------------------------------------------------------------- * * SdlTkRootCoords -- * * Determines the screen coordinates of the top-left corner * of a window, whether it is mapped or not. * * Results: * *x and *y contain the offsets from the top-left of the * root window to the top-left corner of the given window. * * Side effects: * *---------------------------------------------------------------------- */ void SdlTkRootCoords(_Window *_w, int *x, int *y) { int xOff, yOff; xOff = _w->atts.x; yOff = _w->atts.y; while (_w->parent != NULL) { _w = _w->parent; xOff += _w->atts.x; yOff += _w->atts.y; } if (x) { *x = xOff; } if (y) { *y = yOff; } } /* *---------------------------------------------------------------------- * * SdlTkToplevelForWindow -- * * Return the coordinates for a window in its toplevel. * * Results: * Returns the toplevel window containing the given window. * *x and *y (which may be NULL) contain the offsets from * the top-left corner of the toplevel to the top-left * corner of the given window. * * Side effects: * *---------------------------------------------------------------------- */ _Window * SdlTkToplevelForWindow(_Window *_w, int *x, int *y) { int xOff, yOff; if (_w == NULL) { return NULL; } if (IS_ROOT(_w)) { return NULL; } /* Won't usually ask for this (no drawing takes place in wrapper) */ if (PARENT_IS_ROOT(_w)) { if (x) { *x = 0; } if (y) { *y = 0; } return _w; } xOff = _w->atts.x; yOff = _w->atts.y; while (_w->parent && !PARENT_IS_ROOT(_w->parent)) { _w = _w->parent; xOff += _w->atts.x; yOff += _w->atts.y; } if (x) { *x = xOff; } if (y) { *y = yOff; } return _w ? _w->parent : NULL; } /* *---------------------------------------------------------------------- * * SdlTkWrapperForWindow -- * * Get the Tk wrapper for a window. * * Results: * Returns the Tk wrapper window that is an ancestor of the given * window, or, if the given window is a decframe, returns its child * (which must be a wrapper). * * Side effects: * None. * *---------------------------------------------------------------------- */ _Window * SdlTkWrapperForWindow(_Window *_w) { if (IS_ROOT(_w)) { return NULL; } while (!PARENT_IS_ROOT(_w)) { _w = _w->parent; } if (_w->dec != NULL) { _w = _w->child; } return _w; } /* *---------------------------------------------------------------------- * * SdlTkTopVisibleWrapper -- * * Determine the top mapped Tk wrapper. * * Results: * Returns the mapped wrapper highest in the stack, or NULL if * there are no mapped wrapper windows. * * Side effects: * None. * *---------------------------------------------------------------------- */ _Window * SdlTkTopVisibleWrapper(void) { _Window *child = ((_Window *) SdlTkX.screen->root)->child; while (child != NULL) { if (child->atts.map_state != IsUnmapped && !child->atts.override_redirect) { if (child->dec != NULL) { child = child->child; /* the wrapper */ } break; } child = child->next; } return child; } /* *---------------------------------------------------------------------- * * SdlTkGetDrawableSurface -- * * Determines which SDL_Surface should be used to draw into an * X Drawable. * * Results: * If the given Drawable is a pixmap, the result is the pixmap's * own surface. * * The result is the screen's surface. * * Side effects: * None. * *---------------------------------------------------------------------- */ SDL_Surface * SdlTkGetDrawableSurface(Drawable d, int *x, int *y, int *format) { _Pixmap *_p = (_Pixmap *) d; _Window *_w = (_Window *) d; if (_p->type == DT_PIXMAP) { if (x) { *x = 0; } if (y) { *y = 0; } if (format != NULL) { *format = _p->format; } return _p->sdl; } if (IS_ROOT(_w)) { if (x) { *x = 0; } if (y) { *y = 0; } } else { SdlTkRootCoords(_w, x, y); } if (format != NULL) { *format = ((_Window *) (SdlTkX.screen->root))->format; } return SdlTkX.sdlsurf; } /* *---------------------------------------------------------------------- * * SdlTkPointToWindow -- * * Determines which window contains the given x,y location. * * Results: * Returns the deepest descendant containing the given * location; returns the given window if no descendant * contains the location. * * Side effects: * None. * *---------------------------------------------------------------------- */ _Window * SdlTkPointToWindow(_Window *_w, int x, int y, Bool mapped, Bool depth) { _Window *child; for (child = _w->child; child != NULL; child = child->next) { if (x >= child->atts.x && x < child->atts.x + child->parentWidth && y >= child->atts.y && y < child->atts.y + child->parentHeight) { if (!mapped || (child->atts.map_state != IsUnmapped)) { x -= child->atts.x; y -= child->atts.y; if (!depth) { return child; } return SdlTkPointToWindow(child, x, y, mapped, depth); } } } return _w; } /* *---------------------------------------------------------------------- * * SdlTkRemoveFromParent -- * * Remove a window from its parent's list of children. * * Results: * None. * * Side effects: * The window is removed from the parent's list of children. * *---------------------------------------------------------------------- */ void SdlTkRemoveFromParent(_Window *_w) { _Window *child = _w->parent->child, *prev = NULL; /* Find previous */ while (child != NULL) { if (child == _w) { break; } prev = child; child = child->next; } if (child == NULL) { Tcl_Panic("SdlTkRemoveFromParent: can't find %p\n", _w); } if (prev == NULL) { _w->parent->child = _w->next; } else { prev->next = _w->next; } _w->parent = NULL; _w->next = NULL; } /* *---------------------------------------------------------------------- * * SdlTkAddToParent -- * * Add a window to another's list of children. * * Results: * None. * * Side effects: * The window is inserted into the parent's list of children. * *---------------------------------------------------------------------- */ void SdlTkAddToParent(_Window *_w, _Window *parent, _Window *sibling) { _Window *child = parent->child, *prev = NULL; _w->parent = parent; /* Make only child */ if (child == NULL) { parent->child = _w; return; } /* Make last child */ if (sibling == NULL) { while (child->next != NULL) { child = child->next; } child->next = _w; return; } /* Make first child */ if (child == sibling) { _w->next = sibling; parent->child = _w; return; } /* Find previous to sibling */ while (child != NULL) { if (child == sibling) { break; } prev = child; child = child->next; } if (child == NULL) { Tcl_Panic("SdlTkAddToParent: can't find sibling"); } prev->next = _w; _w->next = sibling; } /* *---------------------------------------------------------------------- * * SdlTkRestackWindow -- * * Put a window above or below a sibling. This is the main * window-restacking function. * * Results: * * Side effects: * Stacking order may change, visible regions may change, * events may be added to the event queue. * *---------------------------------------------------------------------- */ void SdlTkRestackWindow(_Window *_w, _Window *sibling, int stack_mode) { _Window *parent = _w->parent; int oldPos = 0, newPos = 0; _Window *child, *oldNext = _w->next;; if (parent->child == _w && _w->next == NULL) { return; } for (child = parent->child; child != _w; child = child->next) { oldPos++; } SdlTkRemoveFromParent(_w); if (sibling == NULL) { switch (stack_mode) { case Above: sibling = parent->child; break; case Below: sibling = parent->child; while (sibling->next != NULL) { sibling = sibling->next; } if (sibling == NULL) { return; } break; } } else { switch (stack_mode) { case Below: sibling = sibling->next; break; } } SdlTkAddToParent(_w, parent, sibling); /* * When restacking a child window, the parent's visible region is * never affected. */ for (child = parent->child; child != _w; child = child->next) { newPos++; } if (oldPos > newPos) { /* Raised */ SdlTkVisRgnChanged(_w, VRC_CHANGED, 0, 0); } else if (oldPos < newPos) { /* Lowered */ SdlTkVisRgnChanged(oldNext, VRC_CHANGED, 0, 0); } } /* *---------------------------------------------------------------------- * * SdlTkRestackTransients -- * * Restack any transient toplevels of the given toplevel so they * are kept in front of it. * * Results: * None. * * Side effects: * Stacking order may change, visible regions may change, * events may be added to the event queue. * *---------------------------------------------------------------------- */ void SdlTkRestackTransients(_Window *_w) { _Window *sibling; if (_w == NULL) { return; } _w = SdlTkToplevelForWindow(_w, NULL, NULL); again: sibling = _w ? _w->next : NULL; while (sibling != NULL) { if (SdlTkWrapperForWindow(sibling)->master == SdlTkWrapperForWindow(_w)) { SdlTkRestackWindow(sibling, _w, Above); SdlTkRestackTransients(sibling); goto again; } sibling = sibling->next; } } /* *---------------------------------------------------------------------- * * SdlTkBringToFrontIfNeeded -- * * Raises the toplevel for the given window above any toplevels * higher in the stacking order which are not transients (or * transients of transients) of the toplevel. * * Results: * None. * * Side effects: * Stacking order may change, visible regions may change, * events may be added to the event queue. * *---------------------------------------------------------------------- */ void SdlTkBringToFrontIfNeeded(_Window *_w) { _Window *sibling, *master; _w = SdlTkToplevelForWindow(_w, NULL, NULL); if (_w == NULL) { return; } sibling = _w->parent->child; while (sibling != _w && SdlTkIsTransientOf(sibling, _w)) sibling = sibling->next; if (sibling != _w) { SdlTkRestackWindow(_w, sibling, Above); SdlTkRestackTransients(_w); } master = SdlTkWrapperForWindow(_w); if (master != NULL) { master = master->master; } if (master != NULL) { SdlTkBringToFrontIfNeeded(master); } } /* *---------------------------------------------------------------------- * * SdlTkIsTransientOf -- * * Determine if a toplevel is a transient (or transient of a * transient) of another toplevel. * * Results: * Returns TRUE if the window is a transient, FALSE otherwise. * * Side effects: * None. * *---------------------------------------------------------------------- */ int SdlTkIsTransientOf(_Window *_w, _Window *other) { _Window *master = SdlTkWrapperForWindow(_w)->master; other = SdlTkWrapperForWindow(other); while (master != NULL) { if (master == other) { return 1; } master = master->master; } return 0; } /* *---------------------------------------------------------------------- * * SdlTkGrabCheck -- * * Determine if a pointer events should be allowed in a window. * * Results: * Returns TRUE if the Tk toplevel for the window has a grab on * the pointer, or if the Tk toplevel is not excluded from any * local or global grab that is in progress. * * Side effects: * None. * *---------------------------------------------------------------------- */ int SdlTkGrabCheck(_Window *_w, int *othergrab) { TkMainInfo *mainInfo; int state = 0; _w = SdlTkWrapperForWindow(_w); *othergrab = 0; /* Get the actual Tk toplevel inside the wrapper */ if (_w->child != NULL) { if (_w->child->next != NULL) { _w = _w->child->next; /* skip menubar */ } else { _w = _w->child; } } if (SdlTkX.keyboard_window != NULL) { /* global grab */ if (SdlTkX.keyboard_window == _w) { return 1; } return 0; } if (SdlTkX.capture_window != NULL) { return SdlTkX.capture_window == _w->tkwin; } if (_w->tkwin != NULL) { state = TkGrabState(_w->tkwin); if (state == TK_GRAB_EXCLUDED) { return 0; } if (state != TK_GRAB_NONE) { return 1; } /* Check console vs. main window */ mainInfo = TkGetMainInfoList(); while (mainInfo != NULL) { if (mainInfo != _w->tkwin->mainPtr) { if (mainInfo->winPtr != NULL) { TkDisplay *dispPtr = mainInfo->winPtr->dispPtr; if (dispPtr->grabWinPtr != NULL) { *othergrab = 1; return 0; } } } mainInfo = mainInfo->nextPtr; } return 1; } return 0; } void SdlTkDirtyAll(Window w) { _Window *_w = (_Window *) w; SdlTkDirtyArea(w, 0, 0, _w->parentWidth, _w->parentHeight); } void SdlTkDirtyArea(Window w, int x, int y, int width, int height) { _Window *_w = (_Window *) w; int xOff, yOff; _Window *top = SdlTkToplevelForWindow(_w, &xOff, &yOff); XRectangle rect; Region rgn; if (top == NULL) { return; } rgn = SdlTkRgnPoolGet(); rect.x = x; rect.y = y; rect.width = width; rect.height = height; XUnionRectWithRegion(&rect, rgn, rgn); XIntersectRegion(_w->visRgn, rgn, rgn); XOffsetRegion(rgn, xOff, yOff); XUnionRegion(rgn, top->dirtyRgn, top->dirtyRgn); SdlTkRgnPoolFree(rgn); } void SdlTkDirtyRegion(Window w, Region rgn) { _Window *_w = (_Window *) w; int xOff, yOff; _Window *top = SdlTkToplevelForWindow(_w, &xOff, &yOff); Region r; if (top == NULL) { return; } r = SdlTkRgnPoolGet(); XIntersectRegion(_w->visRgn, rgn, r); XOffsetRegion(r, xOff, yOff); XUnionRegion(top->dirtyRgn, r, top->dirtyRgn); SdlTkRgnPoolFree(r); } int Tk_SdlTkObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) { Tk_Window tkwin = (Tk_Window) clientData; static const char *optionStrings[] = { "accelbuffer", "accelerometer", "addfont", "android", "expose", "joystick", "maxroot", "paintvisrgn", "powerinfo", "root", "screensaver", "textinput", "touchtranslate", "viewport", "vsync", NULL }; enum options { CMD_ACCELBUFFER, CMD_ACCELEROMETER, CMD_ADDFONT, CMD_ANDROID, CMD_EXPOSE, CMD_JOYSTICK, CMD_MAXROOT, CMD_PAINT_VISRGN, CMD_POWERINFO, CMD_ROOT, CMD_SCREENSAVER, CMD_TEXTINPUT, CMD_TOUCHTRANSLATE, CMD_VIEWPORT, CMD_VSYNC }; static const char *joptStrings[] = { "ids", "guid", "name", "numaxes,", "numballs", "numbuttons", "numhats", NULL }; enum jopts { JOY_IDS, JOY_GUID, JOY_NAME, JOY_NAXES, JOY_NBALLS, JOY_NBUTTONS, JOY_NHATS }; int index; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } switch (index) { /* sdltk expose ?win? */ case CMD_EXPOSE: { int x, y; _Window *_w; Region rgn; XRectangle rect; int ret = TCL_OK; if (objc > 3) { Tcl_WrongNumArgs(interp, 2, objv, "?window?"); return TCL_ERROR; } SdlTkLock(NULL); if (objc == 3) { Tk_Window tkwin2; if (TkGetWindowFromObj(interp, tkwin, objv[2], &tkwin2) != TCL_OK) { ret = TCL_ERROR; goto ce_done; } _w = (_Window *) ((TkWindow *) tkwin2)->window; } else { (void) SDL_GetMouseState(&x, &y); TranslatePointer(0, &x, &y); _w = SdlTkPointToWindow((_Window *) SdlTkX.screen->root, x, y, True, True); } rgn = SdlTkRgnPoolGet(); rect.x = rect.y = 0; rect.width = _w->parentWidth; rect.height = _w->parentHeight; XUnionRectWithRegion(&rect, rgn, rgn); XIntersectRegion(_w->visRgn, rgn, rgn); if (IS_ROOT(_w)) { XUnionRegion(rgn, SdlTkX.screen_dirty_region, SdlTkX.screen_dirty_region); SdlTkScreenChanged(); } else if (_w->tkwin != NULL) { SdlTkGfxExposeRegion((Window) _w, rgn); } SdlTkRgnPoolFree(rgn); ce_done: SdlTkUnlock(NULL); return ret; } /* sdltk paintvisrgn ?win? */ case CMD_PAINT_VISRGN: { int x, y; _Window *_w; Region r; int ret = TCL_OK; if (objc > 3) { Tcl_WrongNumArgs(interp, 2, objv, "?window?"); return TCL_ERROR; } SdlTkLock(NULL); if (objc == 3) { Tk_Window tkwin2; if (TkGetWindowFromObj(interp, tkwin, objv[2], &tkwin2) != TCL_OK) { ret = TCL_ERROR; goto cpv_done; } _w = (_Window *) ((TkWindow *) tkwin2)->window; } else { (void) SDL_GetMouseState(&x, &y); TranslatePointer(0, &x, &y); _w = SdlTkPointToWindow((_Window *) SdlTkX.screen->root, x, y, True, True); } r = SdlTkGetVisibleRegion(_w); SdlTkGfxFillRegion((Drawable) _w, r, 0x0000FF88); SDL_UpdateWindowSurface(SdlTkX.sdlscreen); cpv_done: SdlTkUnlock(NULL); return ret; } /* sdltk textinput ?onoff ?x y ?hints??? */ case CMD_TEXTINPUT: { static int last_hints = 0; int flag = 0, hints = 0, hints_changed = 0, ret = TCL_OK; if (objc > 6) { tiWrongArgs: Tcl_WrongNumArgs(interp, 2, objv, "?onoff ?x y ?hints???"); return TCL_ERROR; } if (objc == 3 || objc == 5 || objc == 6) { if (Tcl_GetBooleanFromObj(interp, objv[2], &flag) != TCL_OK) { return TCL_ERROR; } SdlTkLock(NULL); if (!SDL_HasScreenKeyboardSupport()) { goto ti_done; } if (flag) { if (objc == 5 || objc == 6) { int x, y; SDL_Rect r; if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK || Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) { ret = TCL_ERROR; goto ti_done; } TranslatePointer(1, &x, &y); x -= 64; if (x < 0) { x = 0; } y -= 64; if (y < 0) { y = 0; } r.x = x; r.y = y; r.w = 256; r.h = 128; if (objc > 5) { if (Tcl_GetIntFromObj(interp, objv[5], &hints) != TCL_OK) { ret = TCL_ERROR; goto ti_done; } } if (hints != last_hints) { hints_changed = 1; last_hints = hints; } SDL_SetTextInputRect(&r, hints); } if (hints_changed && SDL_IsScreenKeyboardShown(SdlTkX.sdlscreen)) { SDL_StopTextInput(); } SDL_StartTextInput(); } else { SDL_StopTextInput(); } ti_done: SdlTkUnlock(NULL); return ret; } else if (objc == 4) { goto tiWrongArgs; } SdlTkLock(NULL); if (SDL_HasScreenKeyboardSupport()) { flag = SDL_IsScreenKeyboardShown(SdlTkX.sdlscreen); } SdlTkUnlock(NULL); Tcl_SetBooleanObj(Tcl_GetObjResult(interp), flag); break; } /* sdltk accelerometer ?onoff? */ case CMD_ACCELEROMETER: { int flag = 0; if (objc > 3) { Tcl_WrongNumArgs(interp, 2, objv, "?onoff?"); return TCL_ERROR; } if (objc == 3) { if (Tcl_GetBooleanFromObj(interp, objv[2], &flag) != TCL_OK) { return TCL_ERROR; } #ifdef ANDROID SdlTkLock(NULL); if (SdlTkX.accel_id != -1) { SdlTkX.accel_enabled = flag; } SdlTkUnlock(NULL); #endif break; } #ifdef ANDROID SdlTkLock(NULL); flag = SdlTkX.accel_enabled; SdlTkUnlock(NULL); #endif Tcl_SetBooleanObj(Tcl_GetObjResult(interp), flag); break; } /* sdltk accelbuffer axis */ case CMD_ACCELBUFFER: { int axis; Tcl_Obj *list; if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "axis"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[2], &axis) != TCL_OK) { return TCL_ERROR; } --axis; if ((axis < 0) || (axis > 2)) { Tcl_SetResult(interp, "illegal axis", TCL_STATIC); return TCL_ERROR; } list = Tcl_NewListObj(0, NULL); #ifdef ANDROID SdlTkLock(NULL); if (SdlTkX.accel_id != -1) { AccelRing *rp = &SdlTkX.accel_ring[axis]; int i, k, imax; imax = sizeof (rp->values) / sizeof (rp->values[0]); k = rp->index; for (i = 0; i < imax; i++) { if (++k >= imax) { k = 0; } Tcl_ListObjAppendElement(NULL, list, Tcl_NewIntObj(rp->values[k])); } } SdlTkUnlock(NULL); #endif Tcl_SetObjResult(interp, list); break; } /* sdltk powerinfo */ case CMD_POWERINFO: { SDL_PowerState pst; int secs, pct; char buf[32]; if (objc != 2) { Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } SdlTkLock(NULL); pst = SDL_GetPowerInfo(&secs, &pct); SdlTkUnlock(NULL); Tcl_AppendElement(interp, "state"); switch (pst) { case SDL_POWERSTATE_ON_BATTERY: Tcl_AppendElement(interp, "onbattery"); break; case SDL_POWERSTATE_NO_BATTERY: Tcl_AppendElement(interp, "nobattery"); break; case SDL_POWERSTATE_CHARGING: Tcl_AppendElement(interp, "charging"); break; case SDL_POWERSTATE_CHARGED: Tcl_AppendElement(interp, "charged"); break; default: Tcl_AppendElement(interp, "unknown"); break; } Tcl_AppendElement(interp, "seconds"); sprintf(buf, "%d", secs); Tcl_AppendElement(interp, buf); Tcl_AppendElement(interp, "percent"); sprintf(buf, "%d", pct); Tcl_AppendElement(interp, buf); break; } /* sdltk android */ case CMD_ANDROID: { if (objc != 2) { Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } #ifdef ANDROID Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 1); #else Tcl_SetBooleanObj(Tcl_GetObjResult(interp), 0); #endif break; } /* sdltk touchtranslate ?mask? */ case CMD_TOUCHTRANSLATE: { #ifdef ANDROID int flag = TranslateInfo.enabled; #else int flag = 0; #endif if (objc > 3) { Tcl_WrongNumArgs(interp, 2, objv, "?mask?"); return TCL_ERROR; } if (objc > 2) { if (Tcl_GetIntFromObj(interp, objv[2], &flag) != TCL_OK) { return TCL_ERROR; } #ifdef ANDROID flag &= TRANSLATE_RMB | TRANSLATE_PTZ | TRANSLATE_ZOOM | TRANSLATE_FINGER | TRANSLATE_FBTNS; SdlTkLock(NULL); if (flag != TranslateInfo.enabled) { TranslateInfo.enabled = flag; if (!(flag & TRANSLATE_RMB)) { TranslateInfo.function = NULL; } TranslateInfo.state = 0; TranslateInfo.count = 0; } SdlTkUnlock(NULL); #else flag = 0; #endif } Tcl_SetIntObj(Tcl_GetObjResult(interp), flag); break; } /* sdltk screensaver ?flag? */ case CMD_SCREENSAVER: { int flag; if (objc > 3) { Tcl_WrongNumArgs(interp, 2, objv, "?flag?"); return TCL_ERROR; } if (objc > 2) { if (Tcl_GetBooleanFromObj(interp, objv[2], &flag) != TCL_OK) { return TCL_ERROR; } SdlTkLock(NULL); if (flag) { SDL_EnableScreenSaver(); } else { SDL_DisableScreenSaver(); } SdlTkUnlock(NULL); } SdlTkLock(NULL); flag = SDL_IsScreenSaverEnabled(); SdlTkUnlock(NULL); Tcl_SetBooleanObj(Tcl_GetObjResult(interp), flag); break; } /* sdltk viewport ?x y ?w h?? */ case CMD_VIEWPORT: { int x, y, w, h; char buffer[128]; if (objc != 2 && objc != 4 && objc != 6) { Tcl_WrongNumArgs(interp, 2, objv, "?xoffset yoffset? width height??"); return TCL_ERROR; } if (objc > 2) { if (Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK || Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK) { return TCL_ERROR; } } if (objc > 4) { int sw, sh; if (Tcl_GetIntFromObj(interp, objv[4], &w) != TCL_OK || Tcl_GetIntFromObj(interp, objv[5], &h) != TCL_OK) { return TCL_ERROR; } SdlTkLock(NULL); SDL_GetWindowSize(SdlTkX.sdlscreen, &sw, &sh); SdlTkUnlock(NULL); if (w < 0 || h < 0 || w > sw || h > sh) { Tcl_SetResult(interp, "illegal width or height", TCL_STATIC); return TCL_ERROR; } } if (objc > 2) { SdlTkPanZoom(0, x, y, w, h); } else { SdlTkLock(NULL); sprintf(buffer, "%d %d %d %d", SdlTkX.viewport.x, SdlTkX.viewport.y, SdlTkX.viewport.w, SdlTkX.viewport.h); SdlTkUnlock(NULL); } if (objc <= 2) { Tcl_SetResult(interp, buffer, TCL_VOLATILE); } break; } /* sdltk vsync */ case CMD_VSYNC: { int frame_count; if (objc != 2) { Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } SdlTkLock(NULL); frame_count = SdlTkX.frame_count; SdlTkWaitVSync(); if (SdlTkX.frame_count == frame_count) { SdlTkWaitVSync(); } frame_count -= SdlTkX.frame_count; SdlTkUnlock(NULL); Tcl_SetObjResult(interp, Tcl_NewIntObj(frame_count)); break; } /* sdltk maxroot */ case CMD_MAXROOT: { SDL_RendererInfo ri; char buffer[128]; if (objc != 2) { Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } SdlTkLock(NULL); SDL_GetRendererInfo(SdlTkX.sdlrend, &ri); SdlTkUnlock(NULL); sprintf(buffer, "%d %d", ri.max_texture_width, ri.max_texture_height); Tcl_SetResult(interp, buffer, TCL_VOLATILE); break; } /* sdltk root ?w h? */ case CMD_ROOT: { int w, h; char buffer[128]; if (objc != 2 && objc != 4) { Tcl_WrongNumArgs(interp, 2, objv, "?width height?"); return TCL_ERROR; } if (objc > 2) { SDL_RendererInfo ri; if (Tcl_GetIntFromObj(interp, objv[2], &w) != TCL_OK || Tcl_GetIntFromObj(interp, objv[3], &h) != TCL_OK) { return TCL_ERROR; } SdlTkLock(NULL); SDL_GetRendererInfo(SdlTkX.sdlrend, &ri); SdlTkUnlock(NULL); if (w == 0 && h == 0) { /* accepted, native size */ } else if (w < 200 || h < 200 || w > ri.max_texture_width || h > ri.max_texture_height) { Tcl_SetResult(interp, "unsupported width or height", TCL_STATIC); return TCL_ERROR; } } if (objc > 2) { SdlTkSetRootSize(w, h); } else { SdlTkLock(NULL); sprintf(buffer, "%d %d", SdlTkX.root_w, SdlTkX.root_h); SdlTkUnlock(NULL); } if (objc <= 2) { Tcl_SetResult(interp, buffer, TCL_VOLATILE); } break; } /* sdltk joystick opt ?joyid? */ case CMD_JOYSTICK: { int joy_id; long joy_idl; Tcl_HashEntry *hPtr; SDL_Joystick *stick; if (objc < 3) { Tcl_WrongNumArgs(interp, 2, objv, "suboption ?joyid?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[2], joptStrings, "suboption", 0, &index) != TCL_OK) { return TCL_ERROR; } if (index == JOY_IDS) { Tcl_Obj *result; Tcl_HashSearch search; if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, ""); return TCL_ERROR; } result = Tcl_NewListObj(0, NULL); SdlTkLock(NULL); hPtr = Tcl_FirstHashEntry(&SdlTkX.joystick_table, &search); while (hPtr != NULL) { joy_idl = (long) Tcl_GetHashKey(&SdlTkX.joystick_table, hPtr); Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj((int) joy_idl)); hPtr = Tcl_NextHashEntry(&search); } SdlTkUnlock(NULL); Tcl_SetObjResult(interp, result); return TCL_OK; } if (objc != 4) { Tcl_WrongNumArgs(interp, 3, objv, "joyid"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[3], &joy_id) != TCL_OK) { return TCL_ERROR; } SdlTkLock(NULL); joy_idl = joy_id; hPtr = Tcl_FindHashEntry(&SdlTkX.joystick_table, (ClientData) joy_idl); if (hPtr == NULL) { SdlTkUnlock(NULL); Tcl_SetResult(interp, "unknown joystick identifier", TCL_STATIC); return TCL_ERROR; } stick = (SDL_Joystick *) Tcl_GetHashValue(hPtr); switch (index) { case JOY_NAME: Tcl_SetResult(interp, (char *) SDL_JoystickName(stick), TCL_VOLATILE); break; case JOY_GUID: { SDL_JoystickGUID guid; char buffer[128]; guid = SDL_JoystickGetGUID(stick); sprintf(buffer, "%02x%02x%02x%02x-%02x%02x-%02x%02x" "-%02x%02x-%02x%02x%02x%02x%02x%02x", guid.data[0], guid.data[1], guid.data[2], guid.data[3], guid.data[4], guid.data[5], guid.data[6], guid.data[7], guid.data[8], guid.data[9], guid.data[10], guid.data[11], guid.data[12], guid.data[13], guid.data[14], guid.data[15]); Tcl_SetResult(interp, buffer, TCL_VOLATILE); break; } case JOY_NAXES: Tcl_SetIntObj(Tcl_GetObjResult(interp), SDL_JoystickNumAxes(stick)); break; case JOY_NBALLS: Tcl_SetIntObj(Tcl_GetObjResult(interp), SDL_JoystickNumBalls(stick)); break; case JOY_NHATS: Tcl_SetIntObj(Tcl_GetObjResult(interp), SDL_JoystickNumHats(stick)); break; case JOY_NBUTTONS: Tcl_SetIntObj(Tcl_GetObjResult(interp), SDL_JoystickNumButtons(stick)); break; } SdlTkUnlock(NULL); break; } /* sdltk addfont filename */ case CMD_ADDFONT: { if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "filename"); return TCL_ERROR; } return SdlTkFontAdd(interp, Tcl_GetString(objv[2])); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * TkpGetMS -- * * Return a relative time in milliseconds. It doesn't matter * when the epoch was. * * Results: * Number of milliseconds. * * Side effects: * None. * *---------------------------------------------------------------------- */ unsigned long TkpGetMS(void) { Tcl_Time now; extern void TclpGetTime(Tcl_Time *); TclpGetTime(&now); return now.usec / 1000; } /* NOTE: If this changes, update XAllocColor */ unsigned long TkpGetPixel(XColor *color) { Uint8 r = (color->red / 65535.0) * 255.0; Uint8 g = (color->green / 65535.0) * 255.0; Uint8 b = (color->blue / 65535.0) * 255.0; /* All SDL_gfx xxxColor() routines expect RGBA format */ return SDL_MapRGB(SdlTkX.sdlsurf->format, r, g, b); } /* *---------------------------------------------------------------------- * * TkpSetCaptureEx -- * * This function captures the mouse so that all future events * will be reported to this window, even if the mouse is outside * the window. If the specified window is NULL, then the mouse * is released. * * Results: * None. * * Side effects: * Sets the capture flag and captures the mouse. * *---------------------------------------------------------------------- */ int TkpSetCaptureEx(Display *display, TkWindow *winPtr) { _Window *w0 = NULL, *w1 = NULL; int ret = GrabSuccess; SdlTkLock(display); display->request++; if (winPtr != NULL) { w1 = (_Window *) winPtr->window; } if (SdlTkX.capture_window != NULL) { w0 = (_Window *) SdlTkX.capture_window->window; } if (w0 != NULL && w1 != NULL) { if (w0->display == w1->display) { SdlTkX.capture_window = winPtr; } else { ret = GrabFrozen; } } else if (w0 == NULL) { SdlTkX.capture_window = winPtr; } else if (w1 == NULL) { if (display == w0->display) { SdlTkX.capture_window = winPtr; } else { ret = GrabFrozen; } } SdlTkUnlock(display); return ret; } /* *---------------------------------------------------------------------- * * TkpSetCursor -- * * Set the global cursor. If the cursor is None, then use the * default Tk cursor. * * Results: * None. * * Side effects: * Changes the mouse cursor. * *---------------------------------------------------------------------- */ void SdlTkSetCursor(TkpCursor cursor) { #ifndef ANDROID _Cursor *_c = (_Cursor *) cursor; long shape = SDL_SYSTEM_CURSOR_ARROW; SDL_Cursor *sc = NULL; Tcl_HashEntry *hPtr; if (_c != NULL) { shape = _c->shape; } hPtr = Tcl_FindHashEntry(&SdlTkX.sdlcursors, (ClientData) shape); if (hPtr == NULL) { sc = SDL_CreateSystemCursor(shape); if (sc != NULL) { int isNew; hPtr = Tcl_CreateHashEntry(&SdlTkX.sdlcursors, (ClientData) shape, &isNew); Tcl_SetHashValue(hPtr, (ClientData) sc); } } else { sc = (SDL_Cursor *) Tcl_GetHashValue(hPtr); } if (sc != NULL) { SDL_SetCursor(sc); } #endif } void TkpSetCursor(TkpCursor cursor) { #ifndef ANDROID SdlTkLock(NULL); if (SdlTkX.cursor_change) { SdlTkSetCursor(cursor); } SdlTkUnlock(NULL); #endif } void SdlTkClearPointer(_Window *_w) { if (_w != NULL && _w->tkwin != NULL) { if (SdlTkX.capture_window == _w->tkwin) { SdlTkX.capture_window = NULL; } if (SdlTkX.mouse_window == _w) { SdlTkX.mouse_window = NULL; } } } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */