static void SendKeyboardEvent(GKeyboard *k) { GSourceListener *psl; // Send to the "All Keyboards" source listeners psl = 0; while ((psl = geventGetSourceListener((GSourceHandle)&KeyboardTimer, psl))) SendKeyboardEventToListener(psl, k); // Send to the keyboard specific source listeners psl = 0; while ((psl = geventGetSourceListener((GSourceHandle)k, psl))) SendKeyboardEventToListener(psl, k); }
// The reading callback function static void DialCallback(uint16_t instance, uint16_t rawvalue) { struct DialStatus_t *pds; GSourceListener *psl; GEventDial *pe; /* Get the information we need */ pds = DialStatus+instance; /* Range scale - if needed */ if (pds->max != GINPUT_DIAL_MAX_VALUE) rawvalue = (uint16_t)((uint32_t)rawvalue * pds->max / GINPUT_DIAL_MAX_VALUE); /* Forget about changes below our sensitivity threshold */ if (rawvalue >= pds->lastvalue) { if (rawvalue - pds->lastvalue < pds->sensitivity) return; } else { if (pds->lastvalue - rawvalue < pds->sensitivity) return; } /* Save the value */ pds->lastvalue = rawvalue; // Send the event to the listeners that are interested. psl = 0; while ((psl = geventGetSourceListener((GSourceHandle)(DialStatus+instance), psl))) { if (!(pe = (GEventDial *)geventGetEventBuffer(psl))) continue; pe->type = GEVENT_DIAL; pe->instance = instance; pe->value = pds->lastvalue; geventSendEvent(psl); } }
void _gwinSendEvent(GHandle gh, GEventType type) { GSourceListener * psl; GEventGWin * pge; // Trigger a GWIN Event psl = 0; while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { if (!(pge = (GEventGWin *)geventGetEventBuffer(psl))) continue; pge->type = type; pge->gwin = gh; #if GWIN_WIDGET_TAGS pge->tag = (gh->flags & GWIN_FLG_WIDGET) ? ((GWidgetObject *)gh)->tag : 0; #endif geventSendEvent(psl); } }
// Send the button event static void SendButtonEvent(GWidgetObject *gw) { GSourceListener * psl; GEvent * pe; #define pbe ((GEventGWinButton *)pe) // Trigger a GWIN Button Event psl = 0; while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { if (!(pe = geventGetEventBuffer(psl))) continue; pbe->type = GEVENT_GWIN_BUTTON; pbe->button = (GHandle)gw; geventSendEvent(psl); } #undef pbe }
// Send the checkbox event static void SendCheckboxEvent(GWidgetObject *gw) { GSourceListener * psl; GEvent * pe; #define pce ((GEventGWinCheckbox *)pe) // Trigger a GWIN Checkbox Event psl = 0; while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { if (!(pe = geventGetEventBuffer(psl))) continue; pce->type = GEVENT_GWIN_CHECKBOX; pce->checkbox = &gw->g; pce->isChecked = (gw->g.flags & GCHECKBOX_FLG_CHECKED) ? TRUE : FALSE; geventSendEvent(psl); } #undef pce }
// Send the button event static void SendRadioEvent(GWidgetObject *gw) { GSourceListener * psl; GEvent * pe; #define pbe ((GEventGWinRadio *)pe) // Trigger a GWIN Button Event psl = 0; while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { if (!(pe = geventGetEventBuffer(psl))) continue; pbe->type = GEVENT_GWIN_RADIO; pbe->gwin = (GHandle)gw; pbe->group = ((GRadioObject *)gw)->group; #if GWIN_WIDGET_TAGS pbe->tag = gw->tag; #endif geventSendEvent(psl); } #undef pbe }
static void sendListEvent(GWidgetObject *gw, int item) { GSourceListener* psl; GEvent* pe; // Trigger a GWIN list event psl = 0; while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { if (!(pe = geventGetEventBuffer(psl))) continue; ple->type = GEVENT_GWIN_LIST; ple->gwin = (GHandle)gw; ple->item = item; #if GWIN_WIDGET_TAGS ple->tag = gw->tag; #endif geventSendEvent(psl); } }
static void HighSpeedGTimerCallback(void *param) { (void) param; GSourceListener *psl; GEventADC *pe; psl = 0; while ((psl = geventGetSourceListener((GSourceHandle)(&HighSpeedGTimer), psl))) { if (!(pe = (GEventADC *)geventGetEventBuffer(psl))) { // This listener is missing - save this. psl->srcflags |= GADC_HSADC_LOSTEVENT; continue; } pe->type = GEVENT_ADC; pe->count = hs.lastcount; pe->buffer = hs.lastbuffer; pe->flags = hs.lastflags | psl->srcflags; psl->srcflags = 0; geventSendEvent(psl); } }
static void AudGTimerCallback(void *param) { (void) param; GSourceListener *psl; GEventADC *pe; psl = 0; while ((psl = geventGetSourceListener((GSourceHandle)(&aud), psl))) { if (!(pe = (GEventAudioIn *)geventGetEventBuffer(psl))) { // This listener is missing - save this. psl->srcflags |= GADC_AUDIO_IN_LOSTEVENT; continue; } pe->type = GEVENT_AUDIO_IN; pe->channel = aud.channel; pe->count = lastcount; pe->buffer = lastbuffer; pe->flags = psl->srcflags; psl->srcflags = 0; geventSendEvent(psl); } }
static void MousePoll(void *param) { (void) param; GSourceListener *psl; GEventMouse *pe; unsigned meta; uint16_t tbtns; uint32_t cdiff; uint32_t mdiff; // Save the last mouse state MouseConfig.last_buttons = MouseConfig.t.buttons; // Get the new mouse reading get_calibrated_reading(&MouseConfig.t); // Calculate out new event meta value and handle CLICK and CXTCLICK meta = GMETA_NONE; // Calculate the position difference from our movement reference (update the reference if out of range) mdiff = (MouseConfig.t.x - MouseConfig.movepos.x) * (MouseConfig.t.x - MouseConfig.movepos.x) + (MouseConfig.t.y - MouseConfig.movepos.y) * (MouseConfig.t.y - MouseConfig.movepos.y); if (mdiff > GINPUT_MOUSE_MAX_MOVE_JITTER * GINPUT_MOUSE_MAX_MOVE_JITTER) { MouseConfig.movepos.x = MouseConfig.t.x; MouseConfig.movepos.y = MouseConfig.t.y; } // Check if the click has moved outside the click area and if so cancel the click if ((MouseConfig.flags & FLG_CLICK_TIMER)) { cdiff = (MouseConfig.t.x - MouseConfig.clickpos.x) * (MouseConfig.t.x - MouseConfig.clickpos.x) + (MouseConfig.t.y - MouseConfig.clickpos.y) * (MouseConfig.t.y - MouseConfig.clickpos.y); if (cdiff > GINPUT_MOUSE_MAX_CLICK_JITTER * GINPUT_MOUSE_MAX_CLICK_JITTER) MouseConfig.flags &= ~FLG_CLICK_TIMER; } // Mouse down tbtns = MouseConfig.t.buttons & ~MouseConfig.last_buttons; if ((tbtns & GINPUT_MOUSE_BTN_LEFT)) meta |= GMETA_MOUSE_DOWN; if ((tbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) { MouseConfig.clickpos.x = MouseConfig.t.x; MouseConfig.clickpos.y = MouseConfig.t.y; MouseConfig.clicktime = chTimeNow(); MouseConfig.flags |= FLG_CLICK_TIMER; } // Mouse up tbtns = ~MouseConfig.t.buttons & MouseConfig.last_buttons; if ((tbtns & GINPUT_MOUSE_BTN_LEFT)) meta |= GMETA_MOUSE_UP; if ((tbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) { if ((MouseConfig.flags & FLG_CLICK_TIMER)) { if ((tbtns & GINPUT_MOUSE_BTN_LEFT) #if GINPUT_MOUSE_CLICK_TIME != TIME_INFINITE && chTimeNow() - MouseConfig.clicktime < MS2ST(GINPUT_MOUSE_CLICK_TIME) #endif ) meta |= GMETA_MOUSE_CLICK; else meta |= GMETA_MOUSE_CXTCLICK; MouseConfig.flags &= ~FLG_CLICK_TIMER; } } // Send the event to the listeners that are interested. psl = 0; while ((psl = geventGetSourceListener((GSourceHandle)(&MouseConfig), psl))) { if (!(pe = (GEventMouse *)geventGetEventBuffer(psl))) { // This listener is missing - save the meta events that have happened psl->srcflags |= meta; continue; } // If we haven't really moved (and there are no meta events) don't bother sending the event if (mdiff <= GINPUT_MOUSE_MAX_MOVE_JITTER * GINPUT_MOUSE_MAX_MOVE_JITTER && !psl->srcflags && !meta && !(psl->listenflags & GLISTEN_MOUSENOFILTER)) continue; // Send the event if we are listening for it if (((MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEDOWNMOVES)) || (!(MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEUPMOVES)) || (meta && (psl->listenflags & GLISTEN_MOUSEMETA))) { pe->type = GINPUT_MOUSE_EVENT_TYPE; pe->instance = 0; pe->x = MouseConfig.t.x; pe->y = MouseConfig.t.y; pe->z = MouseConfig.t.z; pe->current_buttons = MouseConfig.t.buttons; pe->last_buttons = MouseConfig.last_buttons; pe->meta = meta; if (psl->srcflags) { pe->current_buttons |= GINPUT_MISSED_MOUSE_EVENT; pe->meta |= psl->srcflags; psl->srcflags = 0; } geventSendEvent(psl); } } }
static void GetMouseReading(GMouse *m) { GMouseReading r; // Step 1 - Get the Raw Reading { m->flags &= ~GMOUSE_FLG_NEEDREAD; if (!gmvmt(m)->get(m, &r)) return; } // Step 2 - Handle touch and button 0 debouncing { // Clean off button garbage r.buttons &= GINPUT_MOUSE_BTN_MASK; #if !GINPUT_TOUCH_NOTOUCH // If touch then calculate button 0 from z if ((gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH)) { if (gmvmt(m)->z_min <= gmvmt(m)->z_max) { if (r.z >= gmvmt(m)->z_touchon) r.buttons |= GINPUT_MOUSE_BTN_LEFT; else if (r.z <= gmvmt(m)->z_touchoff) r.buttons &= ~GINPUT_MOUSE_BTN_LEFT; else return; // bad transitional reading } else { if (r.z <= gmvmt(m)->z_touchon) r.buttons |= GINPUT_MOUSE_BTN_LEFT; else if (r.z >= gmvmt(m)->z_touchoff) r.buttons &= ~GINPUT_MOUSE_BTN_LEFT; else return; // bad transitional reading } } // Devices with poor button 0 transitioning need debouncing if ((gmvmt(m)->d.flags & GMOUSE_VFLG_POORUPDOWN)) { // Are we in a transition test if ((m->flags & GMOUSE_FLG_INDELTA)) { if (!((r.buttons ^ m->r.buttons) & GINPUT_MOUSE_BTN_LEFT)) { // Transition failed m->flags &= ~GMOUSE_FLG_INDELTA; return; } // Transition succeeded m->flags &= ~GMOUSE_FLG_INDELTA; // Should we start a transition test } else if (((r.buttons ^ m->r.buttons) & GINPUT_MOUSE_BTN_LEFT)) { m->flags |= GMOUSE_FLG_INDELTA; return; } } #endif #if !GINPUT_TOUCH_NOCALIBRATE_GUI // Stop here with just the raw x,y reading during calibration if ((m->flags & GMOUSE_FLG_IN_CAL)) { if ((r.buttons & GINPUT_MOUSE_BTN_LEFT)) { m->r.x = r.x; m->r.y = r.y; } m->r.buttons = r.buttons; return; } #endif } // Step 3 - Apply calibration, rotation and display clipping { // If the mouse is up we may need to keep our previous position if ((gmvmt(m)->d.flags & GMOUSE_VFLG_ONLY_DOWN) && !(r.buttons & GINPUT_MOUSE_BTN_LEFT)) { r.x = m->r.x; r.y = m->r.y; } else { #if !GINPUT_TOUCH_NOCALIBRATE // Do we need to calibrate the reading? if ((m->flags & GMOUSE_FLG_CALIBRATE)) CalibrationTransform(&r, &m->caldata); #endif // We can't clip or rotate if we don't have a display if (m->display) { coord_t w, h; // We now need display information w = gdispGGetWidth(m->display); h = gdispGGetHeight(m->display); #if GDISP_NEED_CONTROL // Do we need to rotate the reading to match the display if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) { coord_t t; switch(gdispGGetOrientation(m->display)) { case GDISP_ROTATE_0: break; case GDISP_ROTATE_90: t = r.x; r.x = w - 1 - r.y; r.y = t; break; case GDISP_ROTATE_180: r.x = w - 1 - r.x; r.y = h - 1 - r.y; break; case GDISP_ROTATE_270: t = r.y; r.y = h - 1 - r.x; r.x = t; break; default: break; } } #endif // Do we need to clip the reading to the display if ((m->flags & GMOUSE_FLG_CLIP)) { if (r.x < 0) r.x = 0; else if (r.x >= w) r.x = w-1; if (r.y < 0) r.y = 0; else if (r.y >= h) r.y = h-1; } } } } // Step 4 - Apply jitter detection #if !GINPUT_TOUCH_NOTOUCH { const GMouseJitter *pj; uint32_t diff; // Are we in pen or finger mode pj = (m->flags & GMOUSE_FLG_FINGERMODE) ? &gmvmt(m)->finger_jitter : &gmvmt(m)->pen_jitter; // Is this just movement jitter if (pj->move > 0) { diff = (uint32_t)(r.x - m->r.x) * (uint32_t)(r.x - m->r.x) + (uint32_t)(r.y - m->r.y) * (uint32_t)(r.y - m->r.y); if (diff < (uint32_t)pj->move * (uint32_t)pj->move) { r.x = m->r.x; r.y = m->r.y; } } // Check if the click has moved outside the click area and if so cancel the click if (pj->click > 0 && (m->flags & GMOUSE_FLG_CLICK_TIMER)) { diff = (uint32_t)(r.x - m->clickpos.x) * (uint32_t)(r.x - m->clickpos.x) + (uint32_t)(r.y - m->clickpos.y) * (uint32_t)(r.y - m->clickpos.y); if (diff > (uint32_t)pj->click * (uint32_t)pj->click) m->flags &= ~GMOUSE_FLG_CLICK_TIMER; } } #endif // Step 5 - Click, context-click and other meta event detection { uint16_t upbtns, dnbtns; // Calculate button transitions dnbtns = r.buttons & ~m->r.buttons; upbtns = ~r.buttons & m->r.buttons; // Left mouse down generates the Mouse-down meta event if ((dnbtns & GINPUT_MOUSE_BTN_LEFT)) r.buttons |= GMETA_MOUSE_DOWN; // Left mouse up generates the Mouse-up meta event if ((upbtns & GINPUT_MOUSE_BTN_LEFT)) r.buttons |= GMETA_MOUSE_UP; // Left/Right mouse down starts the click timer if ((dnbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) { m->clickpos.x = r.x; m->clickpos.y = r.y; m->clicktime = gfxSystemTicks(); m->flags |= GMOUSE_FLG_CLICK_TIMER; } // Left/Right mouse up with the click timer still running may generate a click or context click if ((upbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT)) && (m->flags & GMOUSE_FLG_CLICK_TIMER)) { m->flags &= ~GMOUSE_FLG_CLICK_TIMER; m->clicktime = gfxSystemTicks() - m->clicktime; // Was this a short click? if (m->clicktime <= gfxMillisecondsToTicks(GINPUT_MOUSE_CLICK_TIME)) { if ((upbtns & GINPUT_MOUSE_BTN_RIGHT)) r.buttons |= GMETA_MOUSE_CXTCLICK; if ((upbtns & GINPUT_MOUSE_BTN_LEFT)) r.buttons |= GMETA_MOUSE_CLICK; } #if !GINPUT_TOUCH_NOTOUCH // Was this a long click on a touch device? if ((gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH) && m->clicktime >= gfxMillisecondsToTicks(GINPUT_TOUCH_CXTCLICK_TIME)) r.buttons |= GMETA_MOUSE_CXTCLICK; #endif } } // Step 6 - Send the event to the listeners that are interested. { GSourceListener *psl; // Send to the "All Mice" source listeners psl = 0; while ((psl = geventGetSourceListener((GSourceHandle)&MouseTimer, psl))) SendMouseEvent(psl, m, &r); // Send to the mouse specific source listeners psl = 0; while ((psl = geventGetSourceListener((GSourceHandle)m, psl))) SendMouseEvent(psl, m, &r); } // Step 7 - Finally save the results m->r.x = r.x; m->r.y = r.y; m->r.z = r.z; m->r.buttons = r.buttons; }
static DECLARE_THREAD_FUNCTION(visualizerThread, arg) { (void)arg; GListener event_listener; geventListenerInit(&event_listener); geventAttachSource(&event_listener, (GSourceHandle)¤t_status, 0); visualizer_keyboard_status_t initial_status = { .default_layer = 0xFFFFFFFF, .layer = 0xFFFFFFFF, .mods = 0xFF, .leds = 0xFFFFFFFF, .suspended = false, }; visualizer_state_t state = { .status = initial_status, .current_lcd_color = 0, #ifdef LCD_ENABLE .font_fixed5x8 = gdispOpenFont("fixed_5x8"), .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12") #endif }; initialize_user_visualizer(&state); state.prev_lcd_color = state.current_lcd_color; #ifdef LCD_BACKLIGHT_ENABLE lcd_backlight_color( LCD_HUE(state.current_lcd_color), LCD_SAT(state.current_lcd_color), LCD_INT(state.current_lcd_color)); #endif systemticks_t sleep_time = TIME_INFINITE; systemticks_t current_time = gfxSystemTicks(); while(true) { systemticks_t new_time = gfxSystemTicks(); systemticks_t delta = new_time - current_time; current_time = new_time; bool enabled = visualizer_enabled; if (!same_status(&state.status, ¤t_status)) { if (visualizer_enabled) { if (current_status.suspended) { stop_all_keyframe_animations(); visualizer_enabled = false; state.status = current_status; user_visualizer_suspend(&state); } else { state.status = current_status; update_user_visualizer_state(&state); } state.prev_lcd_color = state.current_lcd_color; } } if (!enabled && state.status.suspended && current_status.suspended == false) { // Setting the status to the initial status will force an update // when the visualizer is enabled again state.status = initial_status; state.status.suspended = false; stop_all_keyframe_animations(); user_visualizer_resume(&state); state.prev_lcd_color = state.current_lcd_color; } sleep_time = TIME_INFINITE; for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) { if (animations[i]) { update_keyframe_animation(animations[i], &state, delta, &sleep_time); } } #ifdef LED_ENABLE gdispGFlush(LED_DISPLAY); #endif #ifdef EMULATOR draw_emulator(); #endif // The animation can enable the visualizer // And we might need to update the state when that happens // so don't sleep if (enabled != visualizer_enabled) { sleep_time = 0; } systemticks_t after_update = gfxSystemTicks(); unsigned update_delta = after_update - current_time; if (sleep_time != TIME_INFINITE) { if (sleep_time > update_delta) { sleep_time -= update_delta; } else { sleep_time = 0; } } dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time); #ifdef PROTOCOL_CHIBIOS // The gEventWait function really takes milliseconds, even if the documentation says ticks. // Unfortunately there's no generic ugfx conversion from system time to milliseconds, // so let's do it in a platform dependent way. // On windows the system ticks is the same as milliseconds anyway if (sleep_time != TIME_INFINITE) { sleep_time = ST2MS(sleep_time); } #endif geventEventWait(&event_listener, sleep_time); } #ifdef LCD_ENABLE gdispCloseFont(state.font_fixed5x8); gdispCloseFont(state.font_dejavusansbold12); #endif return 0; } void visualizer_init(void) { gfxInit(); #ifdef LCD_BACKLIGHT_ENABLE lcd_backlight_init(); #endif #ifdef SERIAL_LINK_ENABLE add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) ); #endif #ifdef LCD_ENABLE LCD_DISPLAY = get_lcd_display(); #endif #ifdef LED_ENABLE LED_DISPLAY = get_led_display(); #endif // We are using a low priority thread, the idea is to have it run only // when the main thread is sleeping during the matrix scanning gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack), VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL); } void update_status(bool changed) { if (changed) { GSourceListener* listener = geventGetSourceListener((GSourceHandle)¤t_status, NULL); if (listener) { geventSendEvent(listener); } } #ifdef SERIAL_LINK_ENABLE static systime_t last_update = 0; systime_t current_update = chVTGetSystemTimeX(); systime_t delta = current_update - last_update; if (changed || delta > MS2ST(10)) { last_update = current_update; visualizer_keyboard_status_t* r = begin_write_current_status(); *r = current_status; end_write_current_status(); } #endif } uint8_t visualizer_get_mods() { uint8_t mods = get_mods(); #ifndef NO_ACTION_ONESHOT if (!has_oneshot_mods_timed_out()) { mods |= get_oneshot_mods(); } #endif return mods; } void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds) { // Note that there's a small race condition here, the thread could read // a state where one of these are set but not the other. But this should // not really matter as it will be fixed during the next loop step. // Alternatively a mutex could be used instead of the volatile variables bool changed = false; #ifdef SERIAL_LINK_ENABLE if (is_serial_link_connected ()) { visualizer_keyboard_status_t* new_status = read_current_status(); if (new_status) { if (!same_status(¤t_status, new_status)) { changed = true; current_status = *new_status; } } } else { #else { #endif visualizer_keyboard_status_t new_status = { .layer = state, .default_layer = default_state, .mods = mods, .leds = leds, .suspended = current_status.suspended, }; if (!same_status(¤t_status, &new_status)) { changed = true; current_status = new_status; } } update_status(changed); } void visualizer_suspend(void) { current_status.suspended = true; update_status(true); } void visualizer_resume(void) { current_status.suspended = false; update_status(true); }
static void mouseDown(GWidgetObject *gw, coord_t mx, coord_t my) { GHandle ph, gh; int cnt; if (my < 0 || my > ((GTabsetObject *)gw)->border_top) return; // Work out which tab was pressed { coord_t x, w, y; cnt = 0; x = w = 0; y = GWIN_TABSET_TABHEIGHT; gh = 0; for(ph = gwinGetFirstChild(&gw->g); ph; ph = gwinGetSibling(ph)) { if (ph->vmt == (gwinVMT *)&tabpageVMT) { w = gdispGetStringWidth(((GWidgetObject *)ph)->text, gw->g.font) + TEXT_PADDING*2; x += w; if (x > gw->g.width) { y += GWIN_TABSET_TABHEIGHT; x = w; } if (my < y && mx < x) { gh = ph; break; } cnt++; } } if (!gh || (gh->flags & GWIN_FLG_VISIBLE)) return; } // Mark the existing tab as not visible for(ph = gwinGetFirstChild(&gw->g); ph; ph = gwinGetSibling(ph)) { if (ph->vmt == (gwinVMT *)&tabpageVMT && (ph->flags & GWIN_FLG_VISIBLE)) { // Mark this page invisible ph->flags &= ~GWIN_FLG_VISIBLE; break; } } // Mark this tab as visible gh->flags |= GWIN_FLG_VISIBLE; _gwinRippleVisibility(); // Force a redraw of the whole tabset _gwinUpdate(&gw->g); // Send the Tabset Event { GSourceListener * psl; GEventGWinTabset * pge; psl = 0; while ((psl = geventGetSourceListener(GWIDGET_SOURCE, psl))) { if (!(pge = (GEventGWinTabset *)geventGetEventBuffer(psl))) continue; pge->type = GEVENT_GWIN_TABSET; pge->gwin = &gw->g; #if GWIN_WIDGET_TAGS pge->tag = gw->tag; #endif pge->ghPage = gh; pge->nPage = cnt; geventSendEvent(psl); } } }