/* This is the 'high level' hotspot function that will usually be called * by the widget code. * * It rebuilds the hotspot graph from the current divtree if necessary, finds * the hotspot closest to the mouse pointer, traverses the hotspot graph in * the indicated direction, and finds the new mouse pointer position. * * The direction is a HOTSPOT_* constant */ void hotspot_traverse(short direction) { struct hotspot *p; int x,y; struct divtree *dt; /* rebuild the graph */ if (!hotspotlist) { hotspot_build(dts->top->head,NULL); if (popup_toolbar_passthrough()) hotspot_build(dts->root->head,appmgr_nontoolbar_area()); hotspot_graph(); } /* Create the hotspotnav cursor if it doesn't exist */ if (!dts->top->hotspot_cursor) if (iserror(cursor_new(&dts->top->hotspot_cursor,NULL,-1))) return; /* Find the current node */ cursor_getposition(dts->top->hotspot_cursor,&x,&y,&dt); p = hotspot_closest(x,y); if (!p) return; /* If the closest node isn't really that close, just warp to * that closest node. Otherwise, traverse. * * NOTE: We used to test whether the hotspot cursor was within * the same divnode as the hotspot, but now that the hotspot cursor * is separate from all driver-controlled cursors we can do an * exact match. */ if (x==p->x && y==p->y) { /* traverse */ p = p->graph[direction]; if (!p) return; } /* Make sure the divnode is scrolled in and focused now */ if (p->div) request_focus(p->div->owner); /* Warp the hotspot cursor to the new hotspot location */ cursor_move(dts->top->hotspot_cursor,p->x,p->y,dt); }
void cursor_widgetunder(struct cursor *crsr) { int x,y; struct divnode *div; struct divtree *dt; cursor_getposition(crsr, &x, &y, &dt); div = dt->head; /* If there are popups and they're all in the nontoolbar area, * we can pass toolbar's events through to the bottom layer in the dtstack */ if (popup_toolbar_passthrough()) { struct divnode *ntb = appmgr_nontoolbar_area(); if (ntb) { if (x < ntb->r.x || y < ntb->r.y || x >= ntb->r.x+ntb->r.w || y >= ntb->r.y+ntb->r.h) { /* Get a widget from the bottom layer, with the toolbars */ div = dts->root->head; } } } /* recursively determine the widget/divnode under the cursor */ crsr->ctx.div_under = NULL; crsr->ctx.deepest_div = NULL; r_cursor_widgetunder(crsr,div,x,y); /* Save the widget associated with the divnode we're under */ if (crsr->ctx.div_under) { crsr->ctx.widget_under = hlookup(crsr->ctx.div_under->owner,NULL); /* Also change the cursor theme */ cursor_set_theme(crsr, widget_get(crsr->ctx.div_under->owner,PG_WP_THOBJ)); } else { crsr->ctx.widget_under = 0; /* Default cursor theme */ cursor_set_theme(crsr, PGTH_O_DEFAULT); } }
/* Set the number of cursors occupying the widget, and send any appropriate * triggers due to the change. */ void widget_set_numcursors(struct widget *self, int num, struct cursor *crsr) { union trigparam param; /* Don't let our puny little u8 roll over if something else f***s up */ if (num < 0) num = 0; cursor_getposition(crsr, ¶m.mouse.x, ¶m.mouse.y, NULL); param.mouse.btn = crsr->prev_buttons; param.mouse.chbtn = 0; param.mouse.cursor = crsr; if (self->numcursors && !num) send_trigger(self,PG_TRIGGER_LEAVE,¶m); if (!self->numcursors && num) send_trigger(self,PG_TRIGGER_ENTER,¶m); self->numcursors = num; }
g_error cursor_new(struct cursor **crsr, handle *h, int owner) { g_error e; struct cursor *c; handle c_h; /* Allocate cursor */ e = g_malloc((void**)&c, sizeof(struct cursor)); errorcheck; memset(c,0,sizeof(struct cursor)); /* Default at the screen center */ cursor_getposition(NULL,&c->x, &c->y,NULL); /* Add to list */ c->next = cursor_list; cursor_list = c; /* Allocate the sprite */ e = cursor_set_theme(c, PGTH_O_DEFAULT); errorcheck; /* Give the cursor a handle. We always use pointers to * reference cursors internal to pgserver, but when passing * events to and from a client we must refer to the cursor * using only this handle. */ e = mkhandle(&c_h, PG_TYPE_CURSOR, owner, c); errorcheck; if (crsr) *crsr = c; if (h) *h = c_h; return success; }
void infilter_pntr_dispatch_handler(struct infilter *self, u32 trigger, union trigparam *param) { struct widget *under; int release_captured = 0; int accepted = 0; /* In order to dispatch a pointing event, it must have an associated cursor. * The normalize filter should have given us a cursor whether the driver had * one or not, but in case an event was inserted into the pipe with no cursor, * pass it on. This will usually just run the event off the end of the * filter chain, but this makes it theoretically possible for a client to pick * up the cursorless events. */ if (!param->mouse.cursor) { infilter_send(self, trigger, param); return; } /* Move the cursor */ if (trigger==PG_TRIGGER_MOVE) { int cx,cy; struct divtree *new_dt, *old_dt; /* Default to the topmost divtree, but allow the event to override */ if (iserror(rdhandle((void**)&new_dt, PG_TYPE_DIVTREE, -1, param->mouse.divtree)) || !new_dt) new_dt = dts->top; /* If the cursor is already at the destination, throw away this event */ cursor_getposition(param->mouse.cursor,&cx,&cy,&old_dt); if (cx == param->mouse.x && cy == param->mouse.y && new_dt == old_dt) return; cursor_move(param->mouse.cursor,param->mouse.x,param->mouse.y,new_dt); } inactivity_reset(); /* Read which widget is under the cursor */ under = NULL; rdhandle((void**)&under, PG_TYPE_WIDGET, -1, param->mouse.cursor->ctx.widget_under); /* If the capture_btn is released, release the capture */ if ((!(param->mouse.btn & param->mouse.cursor->ctx.capture_btn)) && param->mouse.cursor->ctx.widget_capture && param->mouse.cursor->ctx.widget_capture != param->mouse.cursor->ctx.widget_under) { struct widget *capture; if ((!iserror(rdhandle((void**)&capture, PG_TYPE_WIDGET, -1, param->mouse.cursor->ctx.widget_capture))) && capture) release_captured = send_trigger(capture,PG_TRIGGER_RELEASE,param); accepted++; param->mouse.cursor->ctx.widget_capture = 0; param->mouse.cursor->ctx.capture_btn = 0; } if (under) { /* There's an interactive widget under the cursor */ /* Keep track of the most recently clicked widget */ if (trigger==PG_TRIGGER_DOWN) { param->mouse.cursor->ctx.widget_last_clicked = param->mouse.cursor->ctx.widget_under; /* Also, allow clicks to focus applications */ appmgr_focus(appmgr_findapp(under)); } /* First send the 'raw' event, then handle the cooked ones. */ if (!release_captured) accepted += send_propagating_trigger(under,trigger,param); /* If the mouse is clicked in a widget, it 'captures' future MOVE and RELEASE events * until this button is released. */ if (trigger==PG_TRIGGER_DOWN && !param->mouse.cursor->ctx.widget_capture) { param->mouse.cursor->ctx.widget_capture = param->mouse.cursor->ctx.widget_under; param->mouse.cursor->ctx.capture_btn = param->mouse.chbtn; } } /* If a captured widget accepts PG_TRIGGER_DRAG, send it even when the * mouse is outside its divnodes. */ if (trigger==PG_TRIGGER_MOVE && param->mouse.cursor->ctx.widget_capture) { struct widget *capture; if ((!iserror(rdhandle((void**)&capture, PG_TYPE_WIDGET, -1, param->mouse.cursor->ctx.widget_capture))) && capture) accepted += send_trigger(capture,PG_TRIGGER_DRAG,param); } /* If nothing has accepted the event so far, pass it on */ if (!accepted) infilter_send(self,trigger,param); }
void infilter_pntr_normalize_handler(struct infilter *self, u32 trigger, union trigparam *param) { int x,y, oldbtn, newbtn; static struct cursor *cursor_global_invisible; /* If we have pointer events with no cursor, assign them our invisible global * cursor so that it can collect context information that we'll need for dispatch. */ if (!param->mouse.cursor) { if (!cursor_global_invisible) { if (iserror(cursor_new(&cursor_global_invisible,NULL,-1))) return; cursor_global_invisible->sprite->visible = 0; } param->mouse.cursor = cursor_global_invisible; } if (trigger != PG_TRIGGER_SCROLLWHEEL) { /* Get physical cursor position for use later */ cursor_getposition(param->mouse.cursor, &x, &y,NULL); if (!param->mouse.is_logical) { /* Normal rotation handling */ VID(coord_physicalize)(&x,&y); } /* Convert relative motion to absolute motion. * We have to be careful about which coordinate system the input is in. */ if (trigger==PG_TRIGGER_PNTR_RELATIVE) { trigger = PG_TRIGGER_PNTR_STATUS; param->mouse.x += x; param->mouse.y += y; if (param->mouse.is_logical) VID(coord_physicalize)(&x,&y); param->mouse.is_logical = 0; } /* Convert absolute motion to individual events */ if (trigger==PG_TRIGGER_PNTR_STATUS) { /* Save the old/new button state too, since it may be modified by other * input filters when we do infilter_send. Remember that this is all working * with the same trigparam structure, and just repassing it after changing * the type :) */ newbtn = param->mouse.btn; oldbtn = param->mouse.cursor->prev_buttons; if (newbtn & ~oldbtn) trigger = PG_TRIGGER_DOWN; else if (oldbtn & ~newbtn) trigger = PG_TRIGGER_UP; else trigger = PG_TRIGGER_MOVE; } /* If we're moving the cursor and this isn't a MOVE event, generate one */ if ((param->mouse.x != x || param->mouse.y != y) && trigger!=PG_TRIGGER_MOVE) { union trigparam moveparam = *param; moveparam.mouse.btn = param->mouse.cursor->prev_buttons; infilter_send(self,PG_TRIGGER_MOVE,&moveparam); } } /* Detect changes in buttons, and store that along with the event */ param->mouse.chbtn = param->mouse.btn ^ param->mouse.cursor->prev_buttons; param->mouse.cursor->prev_buttons = param->mouse.btn; /* Resend it (we might have changed the trigger type) */ infilter_send(self,trigger,param); }
g_error cursor_set_theme(struct cursor *crsr, int thobj) { hwrbitmap *bitmap,*mask; s16 w,h; bool redisplay; g_error e; crsr->thobj = thobj; /* Read the cursor bitmap and bitmask. Note that if the cursor is rectangular * or it uses an alpha channel the mask will be NULL. */ e = rdhandlep((void***)&bitmap,PG_TYPE_BITMAP,-1,theme_lookup(thobj,PGTH_P_CURSORBITMAP)); errorcheck; mask = NULL; e = rdhandlep((void***)&mask,PG_TYPE_BITMAP,-1,theme_lookup(thobj,PGTH_P_CURSORBITMASK)); errorcheck; if (crsr->sprite && bitmap==crsr->sprite->bitmap && mask==crsr->sprite->mask) return success; /* If there's no bitmap yet, it's early in init and the default cursor * hasn't been created yet. Skip this all. */ if (!bitmap) return success; VID(bitmap_getsize) (*bitmap,&w,&h); /* Insert the new bitmaps, resize/create the sprite if necessary */ redisplay = crsr->sprite && crsr->sprite->onscreen; if (redisplay) VID(sprite_hide) (crsr->sprite); /* Update the hotspot before cursor_move */ crsr->hotspot_x = theme_lookup(thobj,PGTH_P_CRSRHOTSPOT_X); crsr->hotspot_y = theme_lookup(thobj,PGTH_P_CRSRHOTSPOT_Y); if (!crsr->sprite) { int x,y; struct divtree *dt; /* Get the default position */ cursor_getposition(crsr,&x,&y,&dt); /* Create a new sprite (default hidden, as per description in input.h) */ e = new_sprite(&crsr->sprite,dt,w,h); errorcheck; crsr->sprite->visible = 0; /* Set the bitmap up, and move it */ crsr->sprite->bitmap = bitmap; crsr->sprite->mask = mask; cursor_move(crsr, x,y, dt); } else { /* Since we're not creating a new sprite, we might need to move the hotspot */ crsr->sprite->x = crsr->x - crsr->hotspot_x; crsr->sprite->y = crsr->y - crsr->hotspot_y; if ( (w!=crsr->sprite->w) || (h!=crsr->sprite->h) ) { /* Resize an existing sprite */ VID(bitmap_free) (crsr->sprite->backbuffer); e = VID(bitmap_new) (&crsr->sprite->backbuffer,w,h,vid->bpp); errorcheck; crsr->sprite->w = w; crsr->sprite->h = h; } crsr->sprite->bitmap = bitmap; crsr->sprite->mask = mask; } /* If the cursor has an alpha channel, set the LGOP accordingly */ if (w && h && (VID(getpixel)(*bitmap,0,0) & PGCF_ALPHA)) crsr->sprite->lgop = PG_LGOP_ALPHA; else crsr->sprite->lgop = PG_LGOP_NONE; if (redisplay) VID(sprite_show)(crsr->sprite); /* Whether it was onscreen or not, show it at the next convenient opportunity */ crsr->sprite->visible = 1; return success; }