Bool XkbComputeRowBounds(XkbGeometryPtr geom, XkbSectionPtr section, XkbRowPtr row) { register int k, pos; XkbKeyPtr key; XkbBoundsPtr bounds, sbounds; if ((!geom) || (!section) || (!row)) return False; bounds = &row->bounds; bzero(bounds, sizeof(XkbBoundsRec)); for (key = row->keys, pos = k = 0; k < row->num_keys; k++, key++) { sbounds = &XkbKeyShape(geom, key)->bounds; _XkbCheckBounds(bounds, pos, 0); if (!row->vertical) { if (key->gap != 0) { pos += key->gap; _XkbCheckBounds(bounds, pos, 0); } _XkbCheckBounds(bounds, pos + sbounds->x1, sbounds->y1); _XkbCheckBounds(bounds, pos + sbounds->x2, sbounds->y2); pos += sbounds->x2; } else { if (key->gap != 0) { pos += key->gap; _XkbCheckBounds(bounds, 0, pos); } _XkbCheckBounds(bounds, pos + sbounds->x1, sbounds->y1); _XkbCheckBounds(bounds, pos + sbounds->x2, sbounds->y2); pos += sbounds->y2; } } return True; }
static guint compute_above_tab_keycode (Display *xdisplay) { XkbDescPtr keyboard; XkbGeometryPtr geometry; int i, j, k; int tab_keycode; char *tab_name; XkbSectionPtr tab_section; XkbBoundsRec tab_bounds; XkbKeyPtr best_key = NULL; guint best_keycode = (guint)-1; int best_x_dist = G_MAXINT; int best_y_dist = G_MAXINT; /* We need only the Names and the Geometry, but asking for these results * in the Keyboard information retrieval failing for unknown reasons. * (Testing with xorg-1.9.1.) So we ask for a part that we don't need * as well. */ keyboard = XkbGetKeyboard (xdisplay, XkbGBN_ClientSymbolsMask | XkbGBN_KeyNamesMask | XkbGBN_GeometryMask, XkbUseCoreKbd); if (!keyboard) return best_keycode; geometry = keyboard->geom; /* There could potentially be multiple keys with the Tab keysym on the keyboard; * but XKeysymToKeycode() returns us the one that the alt-Tab binding will * use which is good enough */ tab_keycode = XKeysymToKeycode (xdisplay, XK_Tab); if (tab_keycode == 0 || tab_keycode < keyboard->min_key_code || tab_keycode > keyboard->max_key_code) goto out; /* The keyboard geometry is stored by key "name" rather than keycode. * (Key names are 4-character strings like like TAB or AE01.) We use the * 'names' part of the keyboard description to map keycode to key name. * * XKB has a "key aliases" feature where a single keyboard key can have * multiple names (with separate sets of aliases in the 'names' part and * in the 'geometry' part), but I don't really understand it or how it is used, * so I'm ignoring it here. */ tab_name = keyboard->names->keys[tab_keycode].name; /* Not NULL terminated! */ /* First, iterate through the keyboard geometry to find the tab key; the keyboard * geometry has a three-level heirarchy of section > row > key */ for (i = 0; i < geometry->num_sections; i++) { XkbSectionPtr section = &geometry->sections[i]; for (j = 0; j < section->num_rows; j++) { int x = 0; int y = 0; XkbRowPtr row = §ion->rows[j]; for (k = 0; k < row->num_keys; k++) { XkbKeyPtr key = &row->keys[k]; XkbShapePtr shape = XkbKeyShape (geometry, key); if (row->vertical) y += key->gap; else x += key->gap; if (strncmp (key->name.name, tab_name, XkbKeyNameLength) == 0) { tab_section = section; tab_bounds = shape->bounds; tab_bounds.x1 += row->left + x; tab_bounds.x2 += row->left + x; tab_bounds.y1 += row->top + y; tab_bounds.y2 += row->top + y; goto found_tab; } if (row->vertical) y += (shape->bounds.y2 - shape->bounds.y1); else x += (shape->bounds.x2 - shape->bounds.x1); } } } /* No tab key found */ goto out; found_tab: /* Now find the key that: * - Is in the same section as the Tab key * - Has a horizontal center in the Tab key's horizonal bounds * - Is above the Tab key at a distance closer than any other key * - In case of ties, has its horizontal center as close as possible * to the Tab key's horizontal center */ for (j = 0; j < tab_section->num_rows; j++) { int x = 0; int y = 0; XkbRowPtr row = &tab_section->rows[j]; for (k = 0; k < row->num_keys; k++) { XkbKeyPtr key = &row->keys[k]; XkbShapePtr shape = XkbKeyShape(geometry, key); XkbBoundsRec bounds = shape->bounds; int x_center; int x_dist, y_dist; if (row->vertical) y += key->gap; else x += key->gap; bounds.x1 += row->left + x; bounds.x2 += row->left + x; bounds.y1 += row->top + y; bounds.y2 += row->top + y; y_dist = tab_bounds.y1 - bounds.y2; if (y_dist < 0) continue; x_center = (bounds.x1 + bounds.x2) / 2; if (x_center < tab_bounds.x1 || x_center > tab_bounds.x2) continue; x_dist = ABS (x_center - (tab_bounds.x1 + tab_bounds.x2) / 2); if (y_dist < best_y_dist || (y_dist == best_y_dist && x_dist < best_x_dist)) { best_key = key; best_x_dist = x_dist; best_y_dist = y_dist; } if (row->vertical) y += (shape->bounds.y2 - shape->bounds.y1); else x += (shape->bounds.x2 - shape->bounds.x1); } } if (best_key == NULL) goto out; /* Now we need to resolve the name of the best key back to a keycode */ for (i = keyboard->min_key_code; i < keyboard->max_key_code; i++) { if (strncmp (best_key->name.name, keyboard->names->keys[i].name, XkbKeyNameLength) == 0) { best_keycode = i; break; } } out: XkbFreeKeyboard (keyboard, 0, True); return best_keycode; }