chiark / gitweb /
Initial revision
[ssr] / StraySrc / Libraries / Steel / c / akbd
1 /*
2  * akbd.c
3  *
4  * Really good keyboard handling
5  *
6  * © 1994-1998 Straylight
7  */
8
9 /*----- Licensing note ----------------------------------------------------*
10  *
11  * This file is part of Straylight's Steel library.
12  *
13  * Steel is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2, or (at your option)
16  * any later version.
17  *
18  * Steel is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with Steel.  If not, write to the Free Software Foundation,
25  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26  */
27
28 #include "os.h"
29 #include "akbd.h"
30 #include "bbc.h"
31
32 /*
33  * BOOL akbd_checkInternalKey(int ikey)
34  *
35  * Use
36  *  Checks whether a given key is being pressed at the moment.
37  *
38  * Parameters
39  *  int ikey == the internal key number of the key to check, as shown on page
40  *    1-849 of the RISC OS 3 Programmer's Reference Manual.
41  */
42
43 BOOL akbd_checkInternalKey(int ikey)
44 {
45   return (bbc_inkey(0xFF00 | (ikey ^ 0xFF)));
46 }
47
48 /*
49  * int akbd_translate(int chcode)
50  *
51  * Use
52  *  Translates the given WIMP-type key number, as returned in a Key_Pressed
53  *  (wimp_EKEY) event into a STEEL extended key number, which should
54  *  distinguish almost every keypress available.
55  *
56  * Parameters
57  *  int chcode == a WIMP-type keypress number
58  *
59  * Returns
60  *  An extended keycode
61  */
62
63 #define akbd__WEIRDCTRL 0x000003BB
64
65 #define akbd__modify(a,b,c,d) \
66   switch (modifiers) \
67   { \
68     case 0: \
69       return (a); \
70     case 1: \
71       return (b); \
72     case 2: \
73       return (c); \
74     case 3: \
75       return (d); \
76   }
77
78 int akbd_translate(int chcode)
79 {
80   int modifiers;
81   int icode=0;
82   int base=0;
83
84   /* --- If it's a function key, the WIMP has translated it for us --- */
85
86   if ((chcode>=0x1CA) ||
87       (chcode>=0x180 && chcode<=0x1BF) ||
88       (chcode>='a' && chcode<='z') ||
89       (chcode>='A' && chcode<='Z') ||
90       (chcode>='[' && chcode<=']'))
91     return (chcode);
92
93   /* --- Work out the modifier status --- */
94
95   modifiers=0;
96   if (akbd_checkInternalKey(0))
97     modifiers|=1;
98   if (akbd_checkInternalKey(1))
99     modifiers|=2;
100
101   /* --- How many ways can you press Delete? --- */
102
103   if (chcode==0x7F)
104   {
105     switch (modifiers)
106     {
107       case 0:
108         return (0x7F);
109       case 1:
110         return (0x17F);
111       case 2:
112         return (0x01F);
113       case 3:
114         return (0x11F);
115     }
116   }
117
118   /* --- Check for variants of space --- */
119
120   if (chcode==' ')
121     akbd__modify(0x020,0x120,0x000,0x100);
122
123   /* --- The many faces of the Return key --- */
124
125   if (chcode==0x0D)
126   {
127     /* --- He pressed return (or maybe ^M, or even Enter...) --- */
128
129     if (akbd_checkInternalKey(60))
130     {
131       /* --- It was Enter, so return a variant of that --- */
132
133       akbd__modify(0x166,0x176,0x126,0x146);
134     }
135     else if (!akbd_checkInternalKey(101))
136     {
137       /* --- It *wasn't* ^M, so it must have been Return --- */
138
139       akbd__modify(0x01D,0x11D,0x13D,0x15D);
140     }
141     /* --- If it was ^M, the control key mashing will catch it --- */
142   }
143
144   /* --- Check for control keys --- */
145
146   if (chcode<0x1B)
147   {
148     if (akbd__WEIRDCTRL & (1<<chcode))
149     {
150       /* --- It's a control key, Jim, but not as we know it... --- */
151
152       static char intCodes[]={0,48,0,17,18,19,0,36,21,38};
153
154       if (chcode==0x008)
155       {
156         /* --- It's backspace, though it may be ^H --- */
157
158         if (!akbd_checkInternalKey(84) &&
159             !akbd_checkInternalKey(21))
160         {
161           /* --- It really is backspace, or a variant thereof --- */
162
163           akbd__modify(0x01C,0x11C,0x13C,0x15C);
164         }
165       }
166       if (chcode)
167       {
168         /* --- It's not a *really* weird key at least --- */
169
170         if (akbd_checkInternalKey(intCodes[chcode]))
171         {
172           /* --- It was a strange ^digit key press --- */
173
174           if (modifiers & 1)
175             return (chcode+0x150);
176           else
177             return (chcode+0x130);
178         }
179       }
180       else
181       {
182         /* --- The many things that return 0 --- */
183
184         if (akbd_checkInternalKey(39))
185           base=0;
186         else
187           base=2;
188         if (modifiers & 1)
189           return (0x150+base);
190         else
191           return (0x130+base);
192       }
193     }
194
195     /* --- Ok -- it's fairly sane then --- */
196
197     if (modifiers & 1)
198       return (chcode+0x100);
199     else
200       return (chcode);
201   }
202
203   /* --- Check for number key presses --- */
204
205   if (chcode>='0' && chcode<='9')
206   {
207     static char keypadTable[]={106,107,124,108,122,123,26,27,42,43};
208
209     if (akbd_checkInternalKey(keypadTable[chcode-'0']))
210     {
211       /* --- It was the keypad version, so we have to modify it --- */
212
213       return (chcode+(0x1C0-'0')+(modifiers<<4));
214     }
215     else
216       return (chcode);
217   }
218
219   /* --- Test for the rest of the keypad --- */
220
221   switch (chcode)
222   {
223     case '/': icode=74; base=0x161; break;
224     case '*': icode=91; base=0x162; break;
225     case '#': icode=90; base=0x163; break;
226     case '-': icode=59; base=0x164; break;
227     case '+': icode=58; base=0x165; break;
228     case '.': icode=76; base=0x167; break;
229   }
230   if (icode)
231   {
232     /* --- It could be a keypad key here, so check --- */
233
234     if (akbd_checkInternalKey(icode))
235     {
236       /* --- It is one of the other keypad keys, so return it modified --- */
237
238       akbd__modify(base,base+16,base-64,base-32);
239     }
240   }
241
242   /* --- The revolting gross (US-ism -- yuk) keys that are left --- */
243
244   switch (chcode)
245   {
246     case 0x01E:
247       /* --- Home, or maybe ^6 :-/ --- */
248
249       if (akbd_checkInternalKey(24))
250       {
251         if (modifiers & 1)
252           return (0x156);
253         else
254           return (0x136);
255       }
256       else
257         akbd__modify(0x01E,0x11E,0x13E,0x15E);
258       break;
259
260     case 0x01B:
261       /* --- Escape, or maybe ^[ :-/ --- */
262
263       if (akbd_checkInternalKey(56))
264       {
265         if (modifiers & 1)
266           return (0x14B);
267         else
268           return (0x12B);
269       }
270       else
271         akbd__modify(0x01B,0x11B,0x13B,0x15B);
272       break;
273
274     case 0x01C:
275     case 0x01D:
276     case 0x01F:
277       if (modifiers & 1)
278         return (chcode+0x130);
279       else
280         return (chcode+0x110);
281       break;
282   }
283
284   /* --- Hmm -- it seems sensible after all --- */
285
286   return (chcode);
287 }
288
289 /*
290  * BOOL akbd_pollsh(void)
291  *
292  * Use
293  *  Returns whether the Shift key is pressed
294  */
295
296 BOOL akbd_pollsh(void)
297 {
298   return (akbd_checkInternalKey(0));
299 }
300
301 /*
302  * BOOL akbd_pollctl(void)
303  *
304  * Use
305  *  Returns whether the Control key is pressed
306  */
307
308 BOOL akbd_pollctl(void)
309 {
310   return (akbd_checkInternalKey(1));
311 }
312
313 /*
314  * BOOL akbd_pollkey(int *code)
315  *
316  * Use
317  *  Reports whether the user has typed ahead, and if so what the keypress
318  *  was.  Note that the keypresses returned are WIMP-type, not STEEL extended
319  *  ones so you will have to use akbd_translate if you need the extended
320  *  type.
321  *
322  *  This call could be used to allow buffering of keypresses: on a
323  *  Key_Pressed event you would call this routine until it returns FALSE
324  *  and store the codes it returns in a buffer along with the code from the
325  *  event.
326  */
327
328 BOOL akbd_pollkey(int *code)
329 {
330   int key;
331
332   /* --- Find out if there is a key waiting --- */
333
334   key=bbc_inkey(0);
335   if (key==-1)
336     return (FALSE);
337
338   /* --- Check for extended codes --- */
339
340   if (!key)
341   {
342     key=bbc_inkey(0);
343     if (key)
344       *code=key+0x100;
345     else
346       *code=0;
347   }
348   else
349     *code=key;
350
351   /* --- We found one --- */
352
353   return (TRUE);
354 }