view finch/libgnt/gntkeys.c @ 16832:9f9c486a8aca

flags of 0x00 in oscar_icon_req() can apparently mean 'you should upload your buddy icon' just as flags of 0x41 can. I'm really unclear as to why Adium with libpurple is getting the 0x00 response while Pidgin with the same libpurple code is getting 0x41 after sending identical data (with identical md5 hashes) for a buddy icon... but that's the way it is. This may fix other conditions in which AIM people claim that their buddy icon isn't being properly set; further insight into what's going on would certainly be appreciated.
author Evan Schoenberg <evan.s@dreskin.net>
date Thu, 03 May 2007 19:12:19 +0000
parents ca463cecd432
children d8b9bea550bc 05d7fe2320a9
line wrap: on
line source

#include "gntkeys.h"

#include <glib.h>
#include <stdlib.h>
#include <string.h>

char *gnt_key_cup;
char *gnt_key_cdown;
char *gnt_key_cleft;
char *gnt_key_cright;

static const char *term;
static GHashTable *specials;

void gnt_init_keys()
{
	const char *controls[] = {"", "c-", "ctrl-", "ctr-", "ctl-", NULL};
	const char *alts[] = {"", "alt-", "a-", "m-", "meta-", NULL};
	int c, a, ch;
	char key[32];

	if (term == NULL) {
		term = getenv("TERM");
		if (!term)
			term = "";  /* Just in case */
	}

	if (strcmp(term, "xterm") == 0 || strcmp(term, "rxvt") == 0) {
		gnt_key_cup    = "\033" "[1;5A";
		gnt_key_cdown  = "\033" "[1;5B";
		gnt_key_cright = "\033" "[1;5C";
		gnt_key_cleft  = "\033" "[1;5D";
	} else if (strcmp(term, "screen") == 0 || strcmp(term, "rxvt-unicode") == 0) {
		gnt_key_cup    = "\033" "Oa";
		gnt_key_cdown  = "\033" "Ob";
		gnt_key_cright = "\033" "Oc";
		gnt_key_cleft  = "\033" "Od";
	}

	specials = g_hash_table_new(g_str_hash, g_str_equal);

#define INSERT_KEY(k, code) do { \
		g_hash_table_insert(specials, g_strdup(k), g_strdup(code)); \
		gnt_keys_add_combination(code); \
	} while (0)

	INSERT_KEY("home",     GNT_KEY_HOME);
	INSERT_KEY("end",      GNT_KEY_END);
	INSERT_KEY("pageup",   GNT_KEY_PGUP);
	INSERT_KEY("pagedown", GNT_KEY_PGDOWN);
	INSERT_KEY("insert",   GNT_KEY_INS);
	INSERT_KEY("delete",   GNT_KEY_DEL);

	INSERT_KEY("left",   GNT_KEY_LEFT);
	INSERT_KEY("right",  GNT_KEY_RIGHT);
	INSERT_KEY("up",     GNT_KEY_UP);
	INSERT_KEY("down",   GNT_KEY_DOWN);

	INSERT_KEY("tab",    "\t");
	INSERT_KEY("menu",   GNT_KEY_POPUP);

	INSERT_KEY("f1",   GNT_KEY_F1);
	INSERT_KEY("f2",   GNT_KEY_F2);
	INSERT_KEY("f3",   GNT_KEY_F3);
	INSERT_KEY("f4",   GNT_KEY_F4);
	INSERT_KEY("f5",   GNT_KEY_F5);
	INSERT_KEY("f6",   GNT_KEY_F6);
	INSERT_KEY("f7",   GNT_KEY_F7);
	INSERT_KEY("f8",   GNT_KEY_F8);
	INSERT_KEY("f9",   GNT_KEY_F9);
	INSERT_KEY("f10",  GNT_KEY_F10);
	INSERT_KEY("f11",  GNT_KEY_F11);
	INSERT_KEY("f12",  GNT_KEY_F12);

#define REM_LENGTH  (sizeof(key) - (cur - key))
#define INSERT_COMB(k, code) do { \
		snprintf(key, sizeof(key), "%s%s%s", controls[c], alts[a], k);  \
		INSERT_KEY(key, code);  \
	} while (0);

	/* Lower-case alphabets */
	for (a = 0, c = 0; controls[c]; c++, a = 0) {
		if (c) {
			INSERT_COMB("up",    gnt_key_cup);
			INSERT_COMB("down",  gnt_key_cdown);
			INSERT_COMB("left",  gnt_key_cleft);
			INSERT_COMB("right", gnt_key_cright);
		}

		for (a = 0; alts[a]; a++) {
			for (ch = 0; ch < 26; ch++) {
				char str[2] = {'a' + ch, 0}, code[4] = "\0\0\0\0";
				int ind = 0;
				if (a)
					code[ind++] = '\033';
				code[ind] = (c ? 1 : 'a') + ch;
				INSERT_COMB(str, code);
			}
		}
	}
	c = 0;
	for (a = 0; alts[a]; a++) {
		/* Upper-case alphabets */
		for (ch = 0; ch < 26; ch++) {
			char str[2] = {'A' + ch, 0}, code[] = {'\033', 'A' + ch, 0};
			INSERT_COMB(str, code);
		}
		/* Digits */
		for (ch = 0; ch < 10; ch++) {
			char str[2] = {'0' + ch, 0}, code[] = {'\033', '0' + ch, 0};
			INSERT_COMB(str, code);
		}
	}
}

void gnt_keys_refine(char *text)
{
	if (*text == 27 && *(text + 1) == '[' &&
			(*(text + 2) >= 'A' && *(text + 2) <= 'D')) {
		/* Apparently this is necessary for urxvt and screen and xterm */
		if (strcmp(term, "screen") == 0 || strcmp(term, "rxvt-unicode") == 0 ||
				strcmp(term, "xterm") == 0)
			*(text + 1) = 'O';
	} else if (*(unsigned char*)text == 195) {
		if (*(text + 2) == 0 && strcmp(term, "xterm") == 0) {
			*(text) = 27;
			*(text + 1) -= 64;  /* Say wha? */
		}
	}
}

const char *gnt_key_translate(const char *name)
{
	return g_hash_table_lookup(specials, name);
}

typedef struct {
	const char *name;
	const char *key;
} gntkey;

static void
get_key_name(gpointer key, gpointer value, gpointer data)
{
	gntkey *k = data;
	if (k->name)
		return;
	if (g_utf8_collate(value, k->key) == 0)
		k->name = key;
}

const char *gnt_key_lookup(const char *key)
{
	gntkey k = {NULL, key};
	g_hash_table_foreach(specials, get_key_name, &k);
	return k.name;
}

/**
 * The key-bindings will be saved in a tree. When a keystroke happens, GNT will
 * find the sequence that matches a binding and return the length.
 * A sequence should not be a prefix of another sequence. If it is, then only
 * the shortest one will be processed. If we want to change that, we will need
 * to allow getting the k-th prefix that matches the input, and pay attention
 * to the return value of gnt_wm_process_input in gntmain.c.
 */
#define SIZE 256

#define IS_END         1 << 0
struct _node
{
	struct _node *next[SIZE];
	int ref;
	int flags;
};

static struct _node root = {.ref = 1, .flags = 0};

static void add_path(struct _node *node, const char *path)
{
	struct _node *n = NULL;
	if (!path || !*path) {
		node->flags |= IS_END;
		return;
	}
	while (*path && node->next[*path]) {
		node = node->next[*path];
		node->ref++;
		path++;
	}
	if (!*path)
		return;
	n = g_new0(struct _node, 1);
	n->ref = 1;
	node->next[*path++] = n;
	add_path(n, path);
}

void gnt_keys_add_combination(const char *path)
{
	add_path(&root, path);
}

static void del_path(struct _node *node, const char *path)
{
	struct _node *next = NULL;

	if (!*path)
		return;
	next = node->next[*path];
	if (!next)
		return;
	del_path(next, path + 1);
	next->ref--;
	if (next->ref == 0) {
		node->next[*path] = NULL;
		g_free(next);
	}
}

void gnt_keys_del_combination(const char *path)
{
	del_path(&root, path);
}

int gnt_keys_find_combination(const char *path)
{
	int depth = 0;
	struct _node *n = &root;

	root.flags &= ~IS_END;
	while (*path && n->next[*path] && !(n->flags & IS_END)) {
		if (!g_ascii_isspace(*path) &&
				!g_ascii_iscntrl(*path) &&
				!g_ascii_isgraph(*path))
			return 0;
		n = n->next[*path++];
		depth++;
	}

	if (!(n->flags & IS_END))
		depth = 0;
	return depth;
}

static void
print_path(struct _node *node, int depth)
{
	int i;
	for (i = 0; i < SIZE; i++) {
		if (node->next[i]) {
			g_printerr("%*c (%d:%d)\n", depth * 4, i, node->next[i]->ref,
						node->next[i]->flags);
			print_path(node->next[i], depth + 1);
		}
	}
}

/* this is purely for debugging purposes. */
void gnt_keys_print_combinations(void);
void gnt_keys_print_combinations()
{
	g_printerr("--------\n");
	print_path(&root, 1);
	g_printerr("--------\n");
}