1 /*
2  * Copyright 2018 Jiri Techet <techet@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "keypress.h"
20 #include "utils.h"
21 
22 #include <gdk/gdkkeysyms.h>
23 
kp_from_event_key(GdkEventKey * ev)24 KeyPress *kp_from_event_key(GdkEventKey *ev)
25 {
26 	KeyPress *kp;
27 
28 	/* ignore keypresses containing Alt and Command on macOS - no Vim command uses them */
29 	if (ev->state & (GDK_MOD1_MASK | GDK_MOD2_MASK))
30 		return NULL;
31 
32 	switch (ev->keyval)
33 	{
34 		case GDK_KEY_Shift_L:
35 		case GDK_KEY_Shift_R:
36 		case GDK_KEY_Control_L:
37 		case GDK_KEY_Control_R:
38 		case GDK_KEY_Caps_Lock:
39 		case GDK_KEY_Shift_Lock:
40 		case GDK_KEY_Meta_L:
41 		case GDK_KEY_Meta_R:
42 		case GDK_KEY_Alt_L:
43 		case GDK_KEY_Alt_R:
44 		case GDK_KEY_Super_L:
45 		case GDK_KEY_Super_R:
46 		case GDK_KEY_Hyper_L:
47 		case GDK_KEY_Hyper_R:
48 			return NULL;
49 	}
50 
51 	kp = g_new0(KeyPress, 1);
52 	kp->key = ev->keyval;
53 	/* We are interested only in Ctrl presses - Alt is not used in Vim and
54 	 * shift is included in letter capitalisation implicitly. The only case
55 	 * we are interested in shift is for insert mode shift+arrow keystrokes. */
56 	switch (ev->keyval)
57 	{
58 		case GDK_KEY_Left:
59 		case GDK_KEY_KP_Left:
60 		case GDK_KEY_leftarrow:
61 		case GDK_KEY_Up:
62 		case GDK_KEY_KP_Up:
63 		case GDK_KEY_uparrow:
64 		case GDK_KEY_Right:
65 		case GDK_KEY_KP_Right:
66 		case GDK_KEY_rightarrow:
67 		case GDK_KEY_Down:
68 		case GDK_KEY_KP_Down:
69 		case GDK_KEY_downarrow:
70 			kp->modif = ev->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
71 			break;
72 		default:
73 			kp->modif = ev->state & GDK_CONTROL_MASK;
74 			break;
75 	}
76 
77 	return kp;
78 }
79 
80 
kp_to_str(KeyPress * kp)81 const gchar *kp_to_str(KeyPress *kp)
82 {
83 	static gchar *utf8 = NULL;
84 	gunichar key = gdk_keyval_to_unicode(kp->key);
85 	gint len;
86 
87 	if (!utf8)
88 		utf8 = g_malloc0(MAX_CHAR_SIZE);
89 	len = g_unichar_to_utf8(key, utf8);
90 	utf8[len] = '\0';
91 	return utf8;
92 }
93 
94 
kp_todigit(KeyPress * kp)95 static gint kp_todigit(KeyPress *kp)
96 {
97 	if (kp->modif != 0)
98 		return -1;
99 
100 	switch (kp->key)
101 	{
102 		case GDK_KEY_0:
103 		case GDK_KEY_KP_0:
104 			return 0;
105 		case GDK_KEY_1:
106 		case GDK_KEY_KP_1:
107 			return 1;
108 		case GDK_KEY_2:
109 		case GDK_KEY_KP_2:
110 			return 2;
111 		case GDK_KEY_3:
112 		case GDK_KEY_KP_3:
113 			return 3;
114 		case GDK_KEY_4:
115 		case GDK_KEY_KP_4:
116 			return 4;
117 		case GDK_KEY_5:
118 		case GDK_KEY_KP_5:
119 			return 5;
120 		case GDK_KEY_6:
121 		case GDK_KEY_KP_6:
122 			return 6;
123 		case GDK_KEY_7:
124 		case GDK_KEY_KP_7:
125 			return 7;
126 		case GDK_KEY_8:
127 		case GDK_KEY_KP_8:
128 			return 8;
129 		case GDK_KEY_9:
130 		case GDK_KEY_KP_9:
131 			return 9;
132 	}
133 	return -1;
134 }
135 
136 
kp_isdigit(KeyPress * kp)137 gboolean kp_isdigit(KeyPress *kp)
138 {
139 	return kp_todigit(kp) != -1;
140 }
141 
142 
kpl_printf(GSList * kpl)143 void kpl_printf(GSList *kpl)
144 {
145 	kpl = g_slist_reverse(kpl);
146 	GSList *pos = kpl;
147 	printf("kpl: ");
148 	while (pos != NULL)
149 	{
150 		KeyPress *kp = pos->data;
151 		printf("<%d>%s", kp->key, kp_to_str(kp));
152 		pos = g_slist_next(pos);
153 	}
154 	printf("\n");
155 	kpl = g_slist_reverse(kpl);
156 }
157 
158 
kpl_get_int(GSList * kpl,GSList ** new_kpl)159 gint kpl_get_int(GSList *kpl, GSList **new_kpl)
160 {
161 	gint res = 0;
162 	gint i = 0;
163 	GSList *pos = kpl;
164 	GSList *num_list = NULL;
165 
166 	if (new_kpl != NULL)
167 		*new_kpl = kpl;
168 
169 	while (pos != NULL)
170 	{
171 		if (kp_isdigit(pos->data))
172 			num_list = g_slist_prepend(num_list, pos->data);
173 		else
174 			break;
175 		pos = g_slist_next(pos);
176 	}
177 
178 	if (!num_list)
179 		return -1;
180 
181 	if (new_kpl != NULL)
182 		*new_kpl = pos;
183 
184 	pos = num_list;
185 	while (pos != NULL)
186 	{
187 		res = res * 10 + kp_todigit(pos->data);
188 		pos = g_slist_next(pos);
189 		i++;
190 		// some sanity check
191 		if (res > 1000000)
192 			break;
193 	}
194 
195 	return res;
196 }
197