/* * Beryl Input-Enabled-Zoom-Plugin * * Copyright : (C) 2006 by Dennis Kasprzyk * E-mail : onestone@beryl-project.org * * Copyright : (C) 2006 Quinn Storm * E-mail : quinnstorm@beryl-project.org * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #define ZOOM_IN_BUTTON_DEFAULT Button4 #define ZOOM_IN_MODIFIERS_DEFAULT CompSuperMask #define ZOOM_OUT_BUTTON_DEFAULT Button5 #define ZOOM_OUT_MODIFIERS_DEFAULT CompSuperMask #define ZOOM_SPEED_DEFAULT 1.00f #define ZOOM_SPEED_MIN 0.1f #define ZOOM_SPEED_MAX 4.0f #define ZOOM_SPEED_PRECISION 0.1f #define ZOOM_STEP_DEFAULT 1.25f #define ZOOM_STEP_MIN 1.05f #define ZOOM_STEP_MAX 5.0f #define ZOOM_STEP_PRECISION 0.05f #define ZOOM_MAX_FACTOR_DEFAULT 16.0f #define ZOOM_MAX_FACTOR_MIN 1.1f #define ZOOM_MAX_FACTOR_MAX 64.0f #define ZOOM_MAX_FACTOR_PRECISION 0.1f #define ZOOM_TIMESTEP_DEFAULT 1.2f #define ZOOM_TIMESTEP_MIN 0.1f #define ZOOM_TIMESTEP_MAX 20.0f #define ZOOM_TIMESTEP_PRECISION 0.1f #define ZOOM_MOUSE_UPDATE_MIN 1 #define ZOOM_MOUSE_UPDATE_MAX 500 #define ZOOM_MOUSE_UPDATE_DEFAULT 10 #define ZOOM_FILTER_LINEAR_DEFAULT TRUE #define ZOOM_HIDE_NORMAL_CURSOR_DEFAULT FALSE #define ZOOM_SHOW_SCALED_CURSOR_DEFAULT FALSE #define ZOOM_OUT_ON_CUBE_DEFAULT TRUE static int displayPrivateIndex; #define ZOOM_DISPLAY_OPTION_IN 0 #define ZOOM_DISPLAY_OPTION_OUT 1 #define ZOOM_DISPLAY_OPTION_NUM 2 typedef struct _ZoomDisplay { int screenPrivateIndex; HandleEventProc handleEvent; Bool fixesSupported; int fixesEventBase; int fixesErrorBase; Bool canHideCursor; CompOption opt[ZOOM_DISPLAY_OPTION_NUM]; } ZoomDisplay; #define ZOOM_SCREEN_OPTION_SPEED 0 #define ZOOM_SCREEN_OPTION_STEP 1 #define ZOOM_SCREEN_OPTION_MAX_FACTOR 2 #define ZOOM_SCREEN_OPTION_TIMESTEP 3 #define ZOOM_SCREEN_OPTION_FILTER_LINEAR 4 #define ZOOM_SCREEN_OPTION_MOUSE_UPDATE 5 #define ZOOM_SCREEN_OPTION_OUT_ON_CUBE 6 #define ZOOM_SCREEN_OPTION_NUM 7 //#define ZOOM_SCREEN_OPTION_HIDE_NORMAL_CURSOR 5 //#define ZOOM_SCREEN_OPTION_SHOW_SCALED_CURSOR 6 typedef struct _CursorTexture { Bool isSet; GLuint texture; int width; int height; int hotX; int hotY; } CursorTexture; typedef struct _ZoomScreen { PreparePaintScreenProc preparePaintScreen; DonePaintScreenProc donePaintScreen; PaintScreenProc paintScreen; PaintTransformedScreenProc paintTransformedScreen; SetScreenOptionForPluginProc setScreenOptionForPlugin; ApplyScreenTransformProc applyScreenTransform; CompTimeoutHandle mouseTimeout; CompOption opt[ZOOM_SCREEN_OPTION_NUM]; float speed; float step; float timestep; float maxScale; GLfloat currentScale; GLfloat newScale; GLfloat toScale; GLfloat zVelocity; GLfloat xscale; GLfloat yscale; Bool active; CursorTexture cursor; int mouseX; int mouseY; Bool cursorHidden; Bool cursorInfoSelected; Bool showScaled; Bool hideNormal; Bool noTimerUpdate; float maxTranslate; } ZoomScreen; #define GET_ZOOM_DISPLAY(d) \ ((ZoomDisplay *) (d)->privates[displayPrivateIndex].ptr) #define ZOOM_DISPLAY(d) \ ZoomDisplay *zd = GET_ZOOM_DISPLAY (d) #define GET_ZOOM_SCREEN(s, zd) \ ((ZoomScreen *) (s)->privates[(zd)->screenPrivateIndex].ptr) #define ZOOM_SCREEN(s) \ ZoomScreen *zs = GET_ZOOM_SCREEN (s, GET_ZOOM_DISPLAY (s->display)) #define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption)) static void updateCursor(Display * dpy, CursorTexture * cursor) { glEnable(GL_TEXTURE_RECTANGLE_ARB); if (!cursor->isSet) { cursor->isSet = TRUE; glGenTextures(1, &cursor->texture); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, cursor->texture); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP); } XFixesCursorImage *ci = XFixesGetCursorImage(dpy); cursor->width = ci->width; cursor->height = ci->height; cursor->hotX = ci->xhot; cursor->hotY = ci->yhot; unsigned char *pixels = malloc(ci->width * ci->height * 4); int i; for (i = 0; i < ci->width * ci->height; i++) { unsigned long pix = ci->pixels[i]; pixels[i * 4] = pix & 0xff; pixels[(i * 4) + 1] = (pix >> 8) & 0xff; pixels[(i * 4) + 2] = (pix >> 16) & 0xff; pixels[(i * 4) + 3] = (pix >> 24) & 0xff; } glBindTexture(GL_TEXTURE_RECTANGLE_ARB, cursor->texture); glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, cursor->width, cursor->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glDisable(GL_TEXTURE_RECTANGLE_ARB); XFree(ci); free(pixels); } static void freeCursor(CursorTexture * cursor) { if (!cursor->isSet) return; cursor->isSet = FALSE; glDeleteTextures(1, &cursor->texture); cursor->texture = 0; } static void zoomHandleEvent(CompDisplay * d, XEvent * event) { CompScreen *s; ZOOM_DISPLAY(d); if (event->type == zd->fixesEventBase + XFixesCursorNotify) { XFixesCursorNotifyEvent *cev = (XFixesCursorNotifyEvent *) event; s = findScreenAtDisplay(d, cev->window); if (s) { ZOOM_SCREEN(s); if (zs->cursor.isSet) updateCursor(d->display, &zs->cursor); } } UNWRAP(zd, d, handleEvent); (*d->handleEvent) (d, event); WRAP(zd, d, handleEvent, zoomHandleEvent); } static Bool zoomSetScreenOption(CompScreen * screen, char *name, CompOptionValue * value) { CompOption *o; int index; ZOOM_SCREEN(screen); o = compFindOption(zs->opt, NUM_OPTIONS(zs), name, &index); if (!o) return FALSE; switch (index) { case ZOOM_SCREEN_OPTION_STEP: if (compSetFloatOption(o, value)) { zs->step = o->value.f; return TRUE; } break; case ZOOM_SCREEN_OPTION_MAX_FACTOR: if (compSetFloatOption(o, value)) { zs->maxScale = o->value.f; return TRUE; } break; case ZOOM_SCREEN_OPTION_SPEED: if (compSetFloatOption(o, value)) { zs->speed = o->value.f; return TRUE; } break; case ZOOM_SCREEN_OPTION_TIMESTEP: if (compSetFloatOption(o, value)) { zs->timestep = o->value.f; return TRUE; } break; case ZOOM_SCREEN_OPTION_FILTER_LINEAR: if (compSetBoolOption(o, value)) return TRUE; /* case ZOOM_SCREEN_OPTION_HIDE_NORMAL_CURSOR: if (compSetBoolOption(o, value)) { zs->hideNormal = o->value.b; return TRUE; } break; case ZOOM_SCREEN_OPTION_SHOW_SCALED_CURSOR: if (compSetBoolOption(o, value)) { zs->showScaled = o->value.b; return TRUE; } break;*/ case ZOOM_SCREEN_OPTION_MOUSE_UPDATE: if (compSetIntOption(o, value)) { return TRUE; } break; case ZOOM_SCREEN_OPTION_OUT_ON_CUBE: if (compSetBoolOption(o, value)) { return TRUE; } break; default: break; } return FALSE; } static void zoomScreenInitOptions(ZoomScreen * zs) { CompOption *o; o = &zs->opt[ZOOM_SCREEN_OPTION_SPEED]; o->advanced = False; o->name = "speed"; o->group = N_("Misc. options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Zoom Speed"); o->longDesc = N_("Zoom Speed."); o->type = CompOptionTypeFloat; o->value.f = ZOOM_SPEED_DEFAULT; o->rest.f.min = ZOOM_SPEED_MIN; o->rest.f.max = ZOOM_SPEED_MAX; o->rest.f.precision = ZOOM_SPEED_PRECISION; o = &zs->opt[ZOOM_SCREEN_OPTION_STEP]; o->advanced = False; o->name = "step"; o->group = N_("Misc. options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Step"); o->longDesc = N_ ("Zoomfactor multiplier for amount of Zooming in each ZoomIn/ZoomOut."); o->type = CompOptionTypeFloat; o->value.f = ZOOM_STEP_DEFAULT; o->rest.f.min = ZOOM_STEP_MIN; o->rest.f.max = ZOOM_STEP_MAX; o->rest.f.precision = ZOOM_STEP_PRECISION; o = &zs->opt[ZOOM_SCREEN_OPTION_MAX_FACTOR]; o->advanced = False; o->name = "max"; o->group = N_("Misc. options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Maximum Zoom"); o->longDesc = N_("Maximum Zoom factor."); o->type = CompOptionTypeFloat; o->value.f = ZOOM_MAX_FACTOR_DEFAULT; o->rest.f.min = ZOOM_MAX_FACTOR_MIN; o->rest.f.max = ZOOM_MAX_FACTOR_MAX; o->rest.f.precision = ZOOM_MAX_FACTOR_PRECISION; o = &zs->opt[ZOOM_SCREEN_OPTION_TIMESTEP]; o->advanced = False; o->name = "timestep"; o->group = N_("Misc. options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Zoom Timestep"); o->longDesc = N_("Zoom Timestep."); o->type = CompOptionTypeFloat; o->value.f = ZOOM_TIMESTEP_DEFAULT; o->rest.f.min = ZOOM_TIMESTEP_MIN; o->rest.f.max = ZOOM_TIMESTEP_MAX; o->rest.f.precision = ZOOM_TIMESTEP_PRECISION; o = &zs->opt[ZOOM_SCREEN_OPTION_FILTER_LINEAR]; o->advanced = False; o->name = "filter_linear"; o->group = N_("Misc. options"); o->subGroup = N_("Visual quality"); o->displayHints = ""; o->shortDesc = N_("Linear Filtering"); o->longDesc = N_("Use Linear Filter when Zoomed in."); o->type = CompOptionTypeBool; o->value.b = ZOOM_FILTER_LINEAR_DEFAULT; /* o = &zs->opt[ZOOM_SCREEN_OPTION_HIDE_NORMAL_CURSOR]; o->advanced = False; o->name = "hide_normal"; o->group = N_("Misc. options"); o->subGroup = N_("Cursor options"); o->displayHints = ""; o->shortDesc = N_("Hide Normal Cursor (May cause\nissues when switching window managers)."); o->longDesc = N_("Hide Normal Cursor during Zoom."); o->type = CompOptionTypeBool; o->value.b = ZOOM_HIDE_NORMAL_CURSOR_DEFAULT; o = &zs->opt[ZOOM_SCREEN_OPTION_SHOW_SCALED_CURSOR]; o->advanced = False; o->name = "show_scaled"; o->group = N_("Misc. options"); o->subGroup = N_("Cursor options"); o->displayHints = ""; o->shortDesc = N_("Show Scaled Cursor"); o->longDesc = N_("Show Scaled Cursor during Zoom."); o->type = CompOptionTypeBool; o->value.b = ZOOM_SHOW_SCALED_CURSOR_DEFAULT;*/ o = &zs->opt[ZOOM_SCREEN_OPTION_MOUSE_UPDATE]; o->advanced = True; o->name = "mouse_update"; o->group = N_("Misc. options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Mouse Update Interval"); o->longDesc = N_("Mouse position Update Interval."); o->type = CompOptionTypeInt; o->value.i = ZOOM_MOUSE_UPDATE_DEFAULT; o->rest.i.min = ZOOM_MOUSE_UPDATE_MIN; o->rest.i.max = ZOOM_MOUSE_UPDATE_MAX; o = &zs->opt[ZOOM_SCREEN_OPTION_OUT_ON_CUBE]; o->advanced = False; o->name = "zoomout_on_cube"; o->group = N_("Misc. options"); o->subGroup = N_(""); o->displayHints = ""; o->shortDesc = N_("Zoom Out on Cube"); o->longDesc = N_("Zoom Out if the Cube gets activated."); o->type = CompOptionTypeBool; o->value.b = ZOOM_OUT_ON_CUBE_DEFAULT; } static CompOption *zoomGetScreenOptions(CompScreen * screen, int *count) { if (screen) { ZOOM_SCREEN(screen); *count = NUM_OPTIONS(zs); return zs->opt; } else { ZoomScreen *zs = malloc(sizeof(ZoomScreen)); zoomScreenInitOptions(zs); *count = NUM_OPTIONS(zs); return zs->opt; } } static int adjustZoomVelocity(ZoomScreen * zs) { float d, adjust, amount; d = (zs->toScale - zs->currentScale) * 75.0f; adjust = d * 0.002f; amount = fabs(d); if (amount < 1.0f) amount = 1.0f; else if (amount > 5.0f) amount = 5.0f; zs->zVelocity = (amount * zs->zVelocity + adjust) / (amount + 1.0f); return (fabs(d) < 0.1f && fabs(zs->zVelocity) < 0.005f); } static void zoomApplyScreenTransform(CompScreen * s, const ScreenPaintAttrib * sAttrib, int output) { ZOOM_SCREEN(s); UNWRAP(zs, s, applyScreenTransform); (*s->applyScreenTransform) (s, sAttrib, output); WRAP(zs, s, applyScreenTransform, zoomApplyScreenTransform); Bool *pCaps = (Bool *) IPCS_GetVPtrND(IPCS_OBJECT(s), "CUBE_PAINTING_CAPS_BOOL_PTR", NULL); if (zs->active && (!pCaps || !(*pCaps))) { float ox, oy, ow, oh; int mmMode = IPCS_GetIntND(IPCS_OBJECT(s), "MM_MODE", 0); if (mmMode == 1) { ox = 0; oy = 0; ow = s->width; oh = s->height; } else { ox = s->outputDev[output].region.extents.x1; oy = s->outputDev[output].region.extents.y1; ow = s->outputDev[output].width; oh = s->outputDev[output].height; } float tx = zs->mouseX - (ox + (ow / 2.0)); float ty = zs->mouseY - (oy + (oh / 2.0)); tx /= ow; tx = -(tx * (zs->xscale - 1)); ty /= oh; ty = ty * (zs->yscale - 1); glTranslatef(tx, ty, 0.0f); glScalef(zs->xscale, zs->yscale, 1.0f); } } static Bool zoomUpdateMouse(void *vs) { CompScreen *s = (CompScreen *) vs; ZOOM_SCREEN(s); if (zs->active && !otherScreenGrabExist(s, "scale", "switcher", "move", "resize", 0)) { int winX, winY; int rootX, rootY; unsigned int mask_return; Window root_return; Window child_return; XQueryPointer(s->display->display, s->root, &root_return, &child_return, &rootX, &rootY, &winX, &winY, &mask_return); if (rootX != zs->mouseX || rootY != zs->mouseY) { zs->mouseX = rootX; zs->mouseY = rootY; damageScreen(s); } } if (!zs->noTimerUpdate) zs->mouseTimeout = compAddTimeout(zs->opt[ZOOM_SCREEN_OPTION_MOUSE_UPDATE].value.i, zoomUpdateMouse, s); return FALSE; } static void zoomPreparePaintScreen(CompScreen * s, int msSinceLastPaint) { ZOOM_SCREEN(s); ZOOM_DISPLAY(s->display); if (screenGrabExist(s, "rotate", "cube", 0) && zs->opt[ZOOM_SCREEN_OPTION_OUT_ON_CUBE].value.b) zs->toScale = 1.0f; else zs->toScale = zs->newScale; if (zs->active) { int steps; float amount, chunk; amount = msSinceLastPaint * 0.05f * zs->speed; steps = amount / (0.5f * zs->timestep); if (!steps) steps = 1; chunk = amount / (float)steps; while (steps--) { if (adjustZoomVelocity(zs)) { zs->currentScale = zs->toScale; zs->zVelocity = 0.0f; } else { zs->currentScale += (zs->zVelocity * msSinceLastPaint) / s->redrawTime; } zs->xscale = zs->currentScale; zs->yscale = zs->currentScale; if (zs->currentScale == 1.0f && zs->zVelocity == 0.0f && zs->newScale == 1.0) { zs->active = FALSE; if (zs->mouseTimeout) { compRemoveTimeout(zs->mouseTimeout); zs->mouseTimeout = 0; } break; } } if (zs->currentScale == 1.0f && zs->zVelocity == 0.0f && zs->newScale == 1.0) { zs->active = FALSE; if (zs->mouseTimeout) { compRemoveTimeout(zs->mouseTimeout); zs->mouseTimeout = 0; } } } if (!zs->active && zd->fixesSupported && zs->cursorInfoSelected) { zs->cursorInfoSelected = FALSE; XFixesSelectCursorInput(s->display->display, s->root, 0); } if (!zs->active && zs->cursor.isSet) { freeCursor(&zs->cursor); } if (zd->canHideCursor && !zs->active && zs->cursorHidden) { zs->cursorHidden = FALSE; XFixesShowCursor(s->display->display, s->root); } UNWRAP(zs, s, preparePaintScreen); (*s->preparePaintScreen) (s, msSinceLastPaint); WRAP(zs, s, preparePaintScreen, zoomPreparePaintScreen); } static void zoomDonePaintScreen(CompScreen * s) { ZOOM_SCREEN(s); if (zs->active && zs->zVelocity != 0.0f) { damageScreen(s); } zs->noTimerUpdate = TRUE; zoomUpdateMouse(s); zs->noTimerUpdate = FALSE; UNWRAP(zs, s, donePaintScreen); (*s->donePaintScreen) (s); WRAP(zs, s, donePaintScreen, zoomDonePaintScreen); } static Bool zoomPaintScreen(CompScreen * s, const ScreenPaintAttrib * sAttrib, Region region, int output, unsigned int mask) { Bool status; ZOOM_SCREEN(s); if (zs->active) { mask &= ~PAINT_SCREEN_REGION_MASK; mask |= PAINT_SCREEN_TRANSFORMED_MASK | PAINT_SCREEN_CLEAR_MASK; int saveFilter = s->filter[SCREEN_TRANS_FILTER]; if (zs->opt[ZOOM_SCREEN_OPTION_FILTER_LINEAR].value.b || zs->zVelocity != 0.0f) s->filter[SCREEN_TRANS_FILTER] = COMP_TEXTURE_FILTER_GOOD; else s->filter[SCREEN_TRANS_FILTER] = COMP_TEXTURE_FILTER_FAST; UNWRAP(zs, s, paintScreen); status = (*s->paintScreen) (s, sAttrib, region, output, mask); WRAP(zs, s, paintScreen, zoomPaintScreen); s->filter[SCREEN_TRANS_FILTER] = saveFilter; } else { UNWRAP(zs, s, paintScreen); status = (*s->paintScreen) (s, sAttrib, region, output, mask); WRAP(zs, s, paintScreen, zoomPaintScreen); } return status; } static void zoomPaintTransformedScreen(CompScreen * s, const ScreenPaintAttrib * sa, Region region, int output, unsigned int mask) { ZOOM_SCREEN(s); if (zs->active) { REGION r; r.rects = &r.extents; r.numRects = 1; if (screenGrabExist(s, "rotate", "cube", 0) && IPCS_GetIntND(IPCS_OBJECT(s), "MM_MODE", 0) == 1) { r.extents.x1 = zs->mouseX - (zs->mouseX / zs->xscale); r.extents.y1 = zs->mouseY - (zs->mouseY / zs->yscale); r.extents.x2 = r.extents.x1 + ceil(s->width / zs->xscale) + 1; r.extents.y2 = r.extents.y1 + ceil(s->height / zs->yscale) + 1; } else { r.extents.x1 = zs->mouseX - (zs->mouseX / zs->xscale) + (s->outputDev[output].region.extents.x1 / zs->xscale); r.extents.y1 = zs->mouseY - (zs->mouseY / zs->yscale) + (s->outputDev[output].region.extents.y1 / zs->yscale); r.extents.x2 = r.extents.x1 + ceil(s->outputDev[output].width / zs->xscale) + 1; r.extents.y2 = r.extents.y1 + ceil(s->outputDev[output].height / zs->yscale) + 1; } r.extents.x1 = MAX(0, r.extents.x1); r.extents.y1 = MAX(0, r.extents.y1); r.extents.x2 = MIN(s->width, r.extents.x2); r.extents.y2 = MIN(s->height, r.extents.y2); UNWRAP(zs, s, paintTransformedScreen); (*s->paintTransformedScreen) (s, sa, &r, output, mask); WRAP(zs, s, paintTransformedScreen, zoomPaintTransformedScreen); if (zs->cursor.isSet) { glPushMatrix(); glLoadIdentity(); prepareXCoords(s, output, -DEFAULT_Z_CAMERA); glTranslatef(zs->mouseX, zs->mouseY, 0.0); glScalef(zs->xscale, zs->yscale, 1.0f); int x = -zs->cursor.hotX; int y = -zs->cursor.hotY; glEnable(GL_BLEND); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, zs->cursor.texture); glEnable(GL_TEXTURE_RECTANGLE_ARB); glBegin(GL_QUADS); glTexCoord2d(0, 0); glVertex2f(x, y); glTexCoord2d(0, zs->cursor.height); glVertex2f(x, y + zs->cursor.height); glTexCoord2d(zs->cursor.width, zs->cursor.height); glVertex2f(x + zs->cursor.width, y + zs->cursor.height); glTexCoord2d(zs->cursor.width, 0); glVertex2f(x + zs->cursor.width, y); glEnd(); glDisable(GL_BLEND); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glDisable(GL_TEXTURE_RECTANGLE_ARB); glPopMatrix(); } } else { UNWRAP(zs, s, paintTransformedScreen); (*s->paintTransformedScreen) (s, sa, region, output, mask); WRAP(zs, s, paintTransformedScreen, zoomPaintTransformedScreen); } } static Bool zoomIn(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompScreen *s; Window xid; ZOOM_DISPLAY(d); xid = getIntOptionNamed(option, nOption, "root", 0); s = findScreenAtDisplay(d, xid); if (s) { ZOOM_SCREEN(s); zs->active = TRUE; if (!zs->mouseTimeout) zs->mouseTimeout = compAddTimeout(zs->opt[ZOOM_SCREEN_OPTION_MOUSE_UPDATE]. value.i, zoomUpdateMouse, s); if (zd->fixesSupported && !zs->cursorInfoSelected && zs->showScaled) { zs->cursorInfoSelected = TRUE; XFixesSelectCursorInput(s->display->display, s->root, XFixesDisplayCursorNotifyMask); updateCursor(d->display, &zs->cursor); } if (zd->canHideCursor && !zs->cursorHidden && zs->hideNormal) { zs->cursorHidden = TRUE; XFixesHideCursor(d->display, s->root); } zs->newScale *= zs->step; if (zs->newScale > zs->maxScale) zs->newScale = zs->maxScale; damageScreen(s); } return TRUE; } static Bool zoomOut(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompScreen *s; Window xid; ZOOM_DISPLAY(d); xid = getIntOptionNamed(option, nOption, "root", 0); s = findScreenAtDisplay(d, xid); if (s) { ZOOM_SCREEN(s); zs->active = TRUE; if (!zs->mouseTimeout) zs->mouseTimeout = compAddTimeout(zs->opt[ZOOM_SCREEN_OPTION_MOUSE_UPDATE]. value.i, zoomUpdateMouse, s); if (zd->fixesSupported && !zs->cursorInfoSelected && zs->showScaled) { zs->cursorInfoSelected = TRUE; XFixesSelectCursorInput(s->display->display, s->root, XFixesDisplayCursorNotifyMask); updateCursor(d->display, &zs->cursor); } if (zd->canHideCursor && !zs->cursorHidden && zs->hideNormal) { zs->cursorHidden = TRUE; XFixesHideCursor(d->display, s->root); } zs->newScale /= zs->step; if (zs->newScale < 1.0) zs->newScale = 1.0; damageScreen(s); } return TRUE; } static void zoomUpdateCubeOptions(CompScreen * s) { CompPlugin *p; ZOOM_SCREEN(s); p = findActivePlugin("cube"); if (p && p->vTable->getScreenOptions) { CompOption *options, *option; int nOptions; options = (*p->vTable->getScreenOptions) (s, &nOptions); option = compFindOption(options, nOptions, "in", 0); if (option) zs->maxTranslate = option->value.b ? 0.85f : 1.5f; } else { zs->maxTranslate = 1.5f; } } static Bool zoomSetScreenOptionForPlugin(CompScreen * s, char *plugin, char *name, CompOptionValue * value) { Bool status; ZOOM_SCREEN(s); UNWRAP(zs, s, setScreenOptionForPlugin); status = (*s->setScreenOptionForPlugin) (s, plugin, name, value); WRAP(zs, s, setScreenOptionForPlugin, zoomSetScreenOptionForPlugin); if (status && strcmp(plugin, "cube") == 0) zoomUpdateCubeOptions(s); return status; } static Bool zoomSetDisplayOption(CompDisplay * display, char *name, CompOptionValue * value) { CompOption *o; int index; ZOOM_DISPLAY(display); o = compFindOption(zd->opt, NUM_OPTIONS(zd), name, &index); if (!o) return FALSE; switch (index) { case ZOOM_DISPLAY_OPTION_IN: case ZOOM_DISPLAY_OPTION_OUT: if (setDisplayAction(display, o, value)) return TRUE; default: break; } return FALSE; } static void zoomDisplayInitOptions(ZoomDisplay * zd) { CompOption *o; o = &zd->opt[ZOOM_DISPLAY_OPTION_IN]; o->advanced = False; o->name = "zoom_in"; o->group = N_("Bindings"); o->subGroup = N_("Zoom In"); o->displayHints = ""; o->shortDesc = N_("Zoom In"); o->longDesc = N_("Zoom In."); o->type = CompOptionTypeAction; o->value.action.initiate = zoomIn; o->value.action.terminate = 0; o->value.action.bell = FALSE; o->value.action.edgeMask = 0; o->value.action.state = CompActionStateInitKey; o->value.action.state |= CompActionStateInitButton; o->value.action.type = CompBindingTypeButton; o->value.action.button.modifiers = ZOOM_IN_MODIFIERS_DEFAULT; o->value.action.button.button = ZOOM_IN_BUTTON_DEFAULT; o = &zd->opt[ZOOM_DISPLAY_OPTION_OUT]; o->advanced = False; o->name = "zoom_out"; o->group = N_("Bindings"); o->subGroup = N_("Zoom Out"); o->displayHints = ""; o->shortDesc = N_("Zoom Out"); o->longDesc = N_("Zoom Out."); o->type = CompOptionTypeAction; o->value.action.initiate = zoomOut; o->value.action.terminate = 0; o->value.action.bell = FALSE; o->value.action.edgeMask = 0; o->value.action.state = CompActionStateInitKey; o->value.action.state |= CompActionStateInitButton; o->value.action.type = CompBindingTypeButton; o->value.action.button.modifiers = ZOOM_OUT_MODIFIERS_DEFAULT; o->value.action.button.button = ZOOM_OUT_BUTTON_DEFAULT; } static CompOption *zoomGetDisplayOptions(CompDisplay * display, int *count) { if (display) { ZOOM_DISPLAY(display); *count = NUM_OPTIONS(zd); return zd->opt; } else { ZoomDisplay *zd = malloc(sizeof(ZoomDisplay)); zoomDisplayInitOptions(zd); *count = NUM_OPTIONS(zd); return zd->opt; } } static Bool zoomInitDisplay(CompPlugin * p, CompDisplay * d) { ZoomDisplay *zd; zd = malloc(sizeof(ZoomDisplay)); if (!zd) return FALSE; zd->screenPrivateIndex = allocateScreenPrivateIndex(d); if (zd->screenPrivateIndex < 0) { free(zd); return FALSE; } zoomDisplayInitOptions(zd); WRAP(zd, d, handleEvent, zoomHandleEvent); d->privates[displayPrivateIndex].ptr = zd; zd->fixesSupported = XFixesQueryExtension(d->display, &zd->fixesEventBase, &zd->fixesErrorBase); int minor, major; XFixesQueryVersion(d->display, &major, &minor); if (major >= 4) zd->canHideCursor = TRUE; else zd->canHideCursor = FALSE; return TRUE; } static void zoomFiniDisplay(CompPlugin * p, CompDisplay * d) { ZOOM_DISPLAY(d); UNWRAP(zd, d, handleEvent); freeScreenPrivateIndex(d, zd->screenPrivateIndex); free(zd); } static Bool zoomInitScreen(CompPlugin * p, CompScreen * s) { ZoomScreen *zs; ZOOM_DISPLAY(s->display); zs = malloc(sizeof(ZoomScreen)); if (!zs) return FALSE; zs->active = FALSE; zs->mouseTimeout = 0; zs->currentScale = 1.0f; zs->newScale = 1.0f; zs->zVelocity = 0.0f; zs->maxTranslate = 0.85f; zs->speed = ZOOM_SPEED_DEFAULT; zs->step = ZOOM_STEP_DEFAULT; zs->maxScale = ZOOM_MAX_FACTOR_DEFAULT; zs->timestep = ZOOM_TIMESTEP_DEFAULT; zoomScreenInitOptions(zs); addScreenAction(s, &zd->opt[ZOOM_DISPLAY_OPTION_IN].value.action); addScreenAction(s, &zd->opt[ZOOM_DISPLAY_OPTION_OUT].value.action); WRAP(zs, s, preparePaintScreen, zoomPreparePaintScreen); WRAP(zs, s, donePaintScreen, zoomDonePaintScreen); WRAP(zs, s, paintScreen, zoomPaintScreen); WRAP(zs, s, paintTransformedScreen, zoomPaintTransformedScreen); WRAP(zs, s, setScreenOptionForPlugin, zoomSetScreenOptionForPlugin); WRAP(zs, s, applyScreenTransform, zoomApplyScreenTransform); s->privates[zd->screenPrivateIndex].ptr = zs; zoomUpdateCubeOptions(s); zs->cursor.isSet = FALSE; zs->cursorHidden = FALSE; zs->cursorInfoSelected = FALSE; zs->showScaled = ZOOM_SHOW_SCALED_CURSOR_DEFAULT; zs->hideNormal = ZOOM_HIDE_NORMAL_CURSOR_DEFAULT; zs->noTimerUpdate = FALSE; return TRUE; } static void zoomFiniScreen(CompPlugin * p, CompScreen * s) { ZOOM_SCREEN(s); ZOOM_DISPLAY(s->display); if (zs->mouseTimeout) { compRemoveTimeout(zs->mouseTimeout); zs->mouseTimeout = 0; } UNWRAP(zs, s, preparePaintScreen); UNWRAP(zs, s, donePaintScreen); UNWRAP(zs, s, paintScreen); UNWRAP(zs, s, paintTransformedScreen); UNWRAP(zs, s, setScreenOptionForPlugin); UNWRAP(zs, s, applyScreenTransform); removeScreenAction(s, &zd->opt[ZOOM_DISPLAY_OPTION_IN].value.action); removeScreenAction(s, &zd->opt[ZOOM_DISPLAY_OPTION_OUT].value.action); free(zs); } static Bool zoomInit(CompPlugin * p) { displayPrivateIndex = allocateDisplayPrivateIndex(); if (displayPrivateIndex < 0) return FALSE; return TRUE; } static void zoomFini(CompPlugin * p) { if (displayPrivateIndex >= 0) freeDisplayPrivateIndex(displayPrivateIndex); } CompPluginDep zoomDeps[] = { {CompPluginRuleBefore, "cube"} , }; CompPluginFeature zoomFeatures[] = { {"zoom"} }; CompPluginVTable zoomVTable = { "inputzoom", N_("Input enabled Zoom"), N_("Input Enabled Zoom"), zoomInit, zoomFini, zoomInitDisplay, zoomFiniDisplay, zoomInitScreen, zoomFiniScreen, 0, 0, zoomGetDisplayOptions, zoomSetDisplayOption, zoomGetScreenOptions, zoomSetScreenOption, zoomDeps, sizeof(zoomDeps) / sizeof(zoomDeps[0]), zoomFeatures, sizeof(zoomFeatures) / sizeof(zoomFeatures[0]), BERYL_ABI_INFO, "beryl-plugins", "accessibility", 0, 0, True, }; CompPluginVTable *getCompPluginInfo(void) { return &zoomVTable; }