+#ifdef KEY_DEBUGGING
+ {
+ int i;
+ printf("keypress: keyval = %04x, state = %08x; string =",
+ event->keyval, event->state);
+ for (i = 0; event->string[i]; i++)
+ printf(" %02x", (unsigned char) event->string[i]);
+ printf("\n");
+ }
+#endif
+
+ /*
+ * NYI: Compose key (!!! requires Unicode faff before even trying)
+ */
+
+ /*
+ * If Alt has just been pressed, we start potentially
+ * accumulating an Alt+numberpad code. We do this by
+ * setting alt_keycode to -1 (nothing yet but plausible).
+ */
+ if ((event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||
+ event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R)) {
+ inst->alt_keycode = -1;
+ inst->alt_digits = 0;
+ goto done; /* this generates nothing else */
+ }
+
+ /*
+ * If we're seeing a numberpad key press with Mod1 down,
+ * consider adding it to alt_keycode if that's sensible.
+ * Anything _else_ with Mod1 down cancels any possibility
+ * of an ALT keycode: we set alt_keycode to -2.
+ */
+ if ((event->state & GDK_MOD1_MASK) && inst->alt_keycode != -2) {
+ int digit = -1;
+ switch (event->keyval) {
+ case GDK_KP_0: case GDK_KP_Insert: digit = 0; break;
+ case GDK_KP_1: case GDK_KP_End: digit = 1; break;
+ case GDK_KP_2: case GDK_KP_Down: digit = 2; break;
+ case GDK_KP_3: case GDK_KP_Page_Down: digit = 3; break;
+ case GDK_KP_4: case GDK_KP_Left: digit = 4; break;
+ case GDK_KP_5: case GDK_KP_Begin: digit = 5; break;
+ case GDK_KP_6: case GDK_KP_Right: digit = 6; break;
+ case GDK_KP_7: case GDK_KP_Home: digit = 7; break;
+ case GDK_KP_8: case GDK_KP_Up: digit = 8; break;
+ case GDK_KP_9: case GDK_KP_Page_Up: digit = 9; break;
+ }
+ if (digit < 0)
+ inst->alt_keycode = -2; /* it's invalid */
+ else {
+#ifdef KEY_DEBUGGING
+ printf("Adding digit %d to keycode %d", digit,
+ inst->alt_keycode);
+#endif
+ if (inst->alt_keycode == -1)
+ inst->alt_keycode = digit; /* one-digit code */
+ else
+ inst->alt_keycode = inst->alt_keycode * 10 + digit;
+ inst->alt_digits++;
+#ifdef KEY_DEBUGGING
+ printf(" gives new code %d\n", inst->alt_keycode);
+#endif
+ /* Having used this digit, we now do nothing more with it. */
+ goto done;
+ }
+ }
+
+ /*
+ * Shift-PgUp and Shift-PgDn don't even generate keystrokes
+ * at all.
+ */
+ if (event->keyval == GDK_Page_Up && (event->state & GDK_SHIFT_MASK)) {
+ term_scroll(inst->term, 0, -cfg.height/2);
+ return TRUE;
+ }
+ if (event->keyval == GDK_Page_Down && (event->state & GDK_SHIFT_MASK)) {
+ term_scroll(inst->term, 0, +cfg.height/2);
+ return TRUE;
+ }
+
+ /*
+ * Neither does Shift-Ins.
+ */
+ if (event->keyval == GDK_Insert && (event->state & GDK_SHIFT_MASK)) {
+ request_paste(inst);
+ return TRUE;
+ }
+
+ /* ALT+things gives leading Escape. */
+ output[0] = '\033';
+ strncpy(output+1, event->string, 31);
+ output[31] = '\0';
+ end = strlen(output);
+ if (event->state & GDK_MOD1_MASK) {
+ start = 0;
+ if (end == 1) end = 0;
+ } else
+ start = 1;
+
+ /* Control-` is the same as Control-\ (unless gtk has a better idea) */
+ if (!event->string[0] && event->keyval == '`' &&
+ (event->state & GDK_CONTROL_MASK)) {
+ output[1] = '\x1C';
+ end = 2;
+ }
+
+ /* Control-Break is the same as Control-C */
+ if (event->keyval == GDK_Break &&
+ (event->state & GDK_CONTROL_MASK)) {
+ output[1] = '\003';
+ end = 2;
+ }
+
+ /* Control-2, Control-Space and Control-@ are NUL */
+ if (!event->string[0] &&
+ (event->keyval == ' ' || event->keyval == '2' ||
+ event->keyval == '@') &&
+ (event->state & (GDK_SHIFT_MASK |
+ GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) {
+ output[1] = '\0';
+ end = 2;
+ }
+
+ /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */
+ if (!event->string[0] && event->keyval == ' ' &&
+ (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) ==
+ (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) {
+ output[1] = '\240';
+ end = 2;
+ }
+
+ /* We don't let GTK tell us what Backspace is! We know better. */
+ if (event->keyval == GDK_BackSpace &&
+ !(event->state & GDK_SHIFT_MASK)) {
+ output[1] = cfg.bksp_is_delete ? '\x7F' : '\x08';
+ end = 2;
+ }
+ /* For Shift Backspace, do opposite of what is configured. */
+ if (event->keyval == GDK_BackSpace &&
+ (event->state & GDK_SHIFT_MASK)) {
+ output[1] = cfg.bksp_is_delete ? '\x08' : '\x7F';
+ end = 2;
+ }
+
+ /* Shift-Tab is ESC [ Z */
+ if (event->keyval == GDK_ISO_Left_Tab ||
+ (event->keyval == GDK_Tab && (event->state & GDK_SHIFT_MASK))) {
+ end = 1 + sprintf(output+1, "\033[Z");
+ }
+
+ /*
+ * NetHack keypad mode.
+ */
+ if (cfg.nethack_keypad) {
+ char *keys = NULL;
+ switch (event->keyval) {
+ case GDK_KP_1: case GDK_KP_End: keys = "bB"; break;
+ case GDK_KP_2: case GDK_KP_Down: keys = "jJ"; break;
+ case GDK_KP_3: case GDK_KP_Page_Down: keys = "nN"; break;
+ case GDK_KP_4: case GDK_KP_Left: keys = "hH"; break;
+ case GDK_KP_5: case GDK_KP_Begin: keys = ".."; break;
+ case GDK_KP_6: case GDK_KP_Right: keys = "lL"; break;
+ case GDK_KP_7: case GDK_KP_Home: keys = "yY"; break;
+ case GDK_KP_8: case GDK_KP_Up: keys = "kK"; break;
+ case GDK_KP_9: case GDK_KP_Page_Up: keys = "uU"; break;
+ }
+ if (keys) {
+ end = 2;
+ if (event->state & GDK_SHIFT_MASK)
+ output[1] = keys[1];
+ else
+ output[1] = keys[0];
+ goto done;
+ }
+ }
+
+ /*
+ * Application keypad mode.
+ */
+ if (inst->term->app_keypad_keys && !cfg.no_applic_k) {
+ int xkey = 0;
+ switch (event->keyval) {
+ case GDK_Num_Lock: xkey = 'P'; break;
+ case GDK_KP_Divide: xkey = 'Q'; break;
+ case GDK_KP_Multiply: xkey = 'R'; break;
+ case GDK_KP_Subtract: xkey = 'S'; break;
+ /*
+ * Keypad + is tricky. It covers a space that would
+ * be taken up on the VT100 by _two_ keys; so we
+ * let Shift select between the two. Worse still,
+ * in xterm function key mode we change which two...
+ */
+ case GDK_KP_Add:
+ if (cfg.funky_type == 2) {
+ if (event->state & GDK_SHIFT_MASK)
+ xkey = 'l';
+ else
+ xkey = 'k';
+ } else if (event->state & GDK_SHIFT_MASK)
+ xkey = 'm';
+ else
+ xkey = 'l';
+ break;
+ case GDK_KP_Enter: xkey = 'M'; break;
+ case GDK_KP_0: case GDK_KP_Insert: xkey = 'p'; break;
+ case GDK_KP_1: case GDK_KP_End: xkey = 'q'; break;
+ case GDK_KP_2: case GDK_KP_Down: xkey = 'r'; break;
+ case GDK_KP_3: case GDK_KP_Page_Down: xkey = 's'; break;
+ case GDK_KP_4: case GDK_KP_Left: xkey = 't'; break;
+ case GDK_KP_5: case GDK_KP_Begin: xkey = 'u'; break;
+ case GDK_KP_6: case GDK_KP_Right: xkey = 'v'; break;
+ case GDK_KP_7: case GDK_KP_Home: xkey = 'w'; break;
+ case GDK_KP_8: case GDK_KP_Up: xkey = 'x'; break;
+ case GDK_KP_9: case GDK_KP_Page_Up: xkey = 'y'; break;
+ case GDK_KP_Decimal: case GDK_KP_Delete: xkey = 'n'; break;
+ }
+ if (xkey) {
+ if (inst->term->vt52_mode) {
+ if (xkey >= 'P' && xkey <= 'S')
+ end = 1 + sprintf(output+1, "\033%c", xkey);
+ else
+ end = 1 + sprintf(output+1, "\033?%c", xkey);
+ } else
+ end = 1 + sprintf(output+1, "\033O%c", xkey);
+ goto done;
+ }
+ }
+
+ /*
+ * Next, all the keys that do tilde codes. (ESC '[' nn '~',
+ * for integer decimal nn.)
+ *
+ * We also deal with the weird ones here. Linux VCs replace F1
+ * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
+ * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
+ * respectively.
+ */
+ {
+ int code = 0;
+ switch (event->keyval) {
+ case GDK_F1:
+ code = (event->state & GDK_SHIFT_MASK ? 23 : 11);
+ break;
+ case GDK_F2:
+ code = (event->state & GDK_SHIFT_MASK ? 24 : 12);
+ break;
+ case GDK_F3:
+ code = (event->state & GDK_SHIFT_MASK ? 25 : 13);
+ break;
+ case GDK_F4:
+ code = (event->state & GDK_SHIFT_MASK ? 26 : 14);
+ break;
+ case GDK_F5:
+ code = (event->state & GDK_SHIFT_MASK ? 28 : 15);
+ break;
+ case GDK_F6:
+ code = (event->state & GDK_SHIFT_MASK ? 29 : 17);
+ break;
+ case GDK_F7:
+ code = (event->state & GDK_SHIFT_MASK ? 31 : 18);
+ break;
+ case GDK_F8:
+ code = (event->state & GDK_SHIFT_MASK ? 32 : 19);
+ break;
+ case GDK_F9:
+ code = (event->state & GDK_SHIFT_MASK ? 33 : 20);
+ break;
+ case GDK_F10:
+ code = (event->state & GDK_SHIFT_MASK ? 34 : 21);
+ break;
+ case GDK_F11:
+ code = 23;
+ break;
+ case GDK_F12:
+ code = 24;
+ break;
+ case GDK_F13:
+ code = 25;
+ break;
+ case GDK_F14:
+ code = 26;
+ break;
+ case GDK_F15:
+ code = 28;
+ break;
+ case GDK_F16:
+ code = 29;
+ break;
+ case GDK_F17:
+ code = 31;
+ break;
+ case GDK_F18:
+ code = 32;
+ break;
+ case GDK_F19:
+ code = 33;
+ break;
+ case GDK_F20:
+ code = 34;
+ break;
+ }
+ if (!(event->state & GDK_CONTROL_MASK)) switch (event->keyval) {
+ case GDK_Home: case GDK_KP_Home:
+ code = 1;
+ break;
+ case GDK_Insert: case GDK_KP_Insert:
+ code = 2;
+ break;
+ case GDK_Delete: case GDK_KP_Delete:
+ code = 3;
+ break;
+ case GDK_End: case GDK_KP_End:
+ code = 4;
+ break;
+ case GDK_Page_Up: case GDK_KP_Page_Up:
+ code = 5;
+ break;
+ case GDK_Page_Down: case GDK_KP_Page_Down:
+ code = 6;
+ break;
+ }
+ /* Reorder edit keys to physical order */
+ if (cfg.funky_type == 3 && code <= 6)
+ code = "\0\2\1\4\5\3\6"[code];
+
+ if (inst->term->vt52_mode && code > 0 && code <= 6) {
+ end = 1 + sprintf(output+1, "\x1B%c", " HLMEIG"[code]);
+ goto done;
+ }
+
+ if (cfg.funky_type == 5 && /* SCO function keys */
+ code >= 11 && code <= 34) {
+ char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
+ int index = 0;
+ switch (event->keyval) {
+ case GDK_F1: index = 0; break;
+ case GDK_F2: index = 1; break;
+ case GDK_F3: index = 2; break;
+ case GDK_F4: index = 3; break;
+ case GDK_F5: index = 4; break;
+ case GDK_F6: index = 5; break;
+ case GDK_F7: index = 6; break;
+ case GDK_F8: index = 7; break;
+ case GDK_F9: index = 8; break;
+ case GDK_F10: index = 9; break;
+ case GDK_F11: index = 10; break;
+ case GDK_F12: index = 11; break;
+ }
+ if (event->state & GDK_SHIFT_MASK) index += 12;
+ if (event->state & GDK_CONTROL_MASK) index += 24;
+ end = 1 + sprintf(output+1, "\x1B[%c", codes[index]);
+ goto done;
+ }
+ if (cfg.funky_type == 5 && /* SCO small keypad */
+ code >= 1 && code <= 6) {
+ char codes[] = "HL.FIG";
+ if (code == 3) {
+ output[1] = '\x7F';
+ end = 2;
+ } else {
+ end = 1 + sprintf(output+1, "\x1B[%c", codes[code-1]);
+ }
+ goto done;
+ }
+ if ((inst->term->vt52_mode || cfg.funky_type == 4) &&
+ code >= 11 && code <= 24) {
+ int offt = 0;
+ if (code > 15)
+ offt++;
+ if (code > 21)
+ offt++;
+ if (inst->term->vt52_mode)
+ end = 1 + sprintf(output+1,
+ "\x1B%c", code + 'P' - 11 - offt);
+ else
+ end = 1 + sprintf(output+1,
+ "\x1BO%c", code + 'P' - 11 - offt);
+ goto done;
+ }
+ if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
+ end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11);
+ goto done;
+ }
+ if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
+ if (inst->term->vt52_mode)
+ end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11);
+ else
+ end = 1 + sprintf(output+1, "\x1BO%c", code + 'P' - 11);
+ goto done;
+ }
+ if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
+ end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw");
+ goto done;
+ }
+ if (code) {
+ end = 1 + sprintf(output+1, "\x1B[%d~", code);
+ goto done;
+ }
+ }
+
+ /*
+ * Cursor keys. (This includes the numberpad cursor keys,
+ * if we haven't already done them due to app keypad mode.)
+ *
+ * Here we also process un-numlocked un-appkeypadded KP5,
+ * which sends ESC [ G.
+ */
+ {
+ int xkey = 0;
+ switch (event->keyval) {
+ case GDK_Up: case GDK_KP_Up: xkey = 'A'; break;
+ case GDK_Down: case GDK_KP_Down: xkey = 'B'; break;
+ case GDK_Right: case GDK_KP_Right: xkey = 'C'; break;
+ case GDK_Left: case GDK_KP_Left: xkey = 'D'; break;
+ case GDK_Begin: case GDK_KP_Begin: xkey = 'G'; break;
+ }
+ if (xkey) {
+ /*
+ * The arrow keys normally do ESC [ A and so on. In
+ * app cursor keys mode they do ESC O A instead.
+ * Ctrl toggles the two modes.
+ */
+ if (inst->term->vt52_mode) {
+ end = 1 + sprintf(output+1, "\033%c", xkey);
+ } else if (!inst->term->app_cursor_keys ^
+ !(event->state & GDK_CONTROL_MASK)) {
+ end = 1 + sprintf(output+1, "\033O%c", xkey);
+ } else {
+ end = 1 + sprintf(output+1, "\033[%c", xkey);
+ }
+ goto done;
+ }
+ }
+ goto done;
+ }
+
+ done:
+
+ if (end-start > 0) {
+#ifdef KEY_DEBUGGING
+ int i;
+ printf("generating sequence:");
+ for (i = start; i < end; i++)
+ printf(" %02x", (unsigned char) output[i]);
+ printf("\n");
+#endif
+
+ ldisc_send(inst->ldisc, output+start, end-start, 1);
+ show_mouseptr(inst, 0);
+ term_seen_key_event(inst->term);
+ term_out(inst->term);