1 module unde.keybar.lib;
2 
3 import berkeleydb.all;
4 
5 import derelict.sdl2.sdl;
6 import derelict.sdl2.ttf;
7 import derelict.sdl2.image;
8 
9 import unde.global_state;
10 import unde.font;
11 import unde.viewers.image_viewer.lib;
12 import unde.command_line.lib;
13 import unde.slash;
14 
15 import std.stdio;
16 import std..string;
17 import std.math;
18 import std.range.primitives;
19 import std.file;
20 import std.algorithm.sorting;
21 import std.process;
22 import core.stdc.locale;
23 import core.sys.windows.windows;
24 
25 struct KeyHandler
26 {
27     void delegate (GlobalState gs) handler;
28     string description;
29     string icon;
30 }
31 
32 struct ButtonParms
33 {
34     SDL_Rect rect;
35     SDL_Color color;
36 }
37 
38 struct Layout
39 {
40     string short_name;
41     string name;
42 
43     string[][3] letters;
44     string[][3] letters_shift;
45     string[][3] letters_altgr;
46     string[][3] letters_shift_altgr;
47 }
48 
49 enum LayoutChanger
50 {
51     Ctrl_Shift = Modifiers.Left_Ctrl | Modifiers.Left_Shift,
52     LeftAlt = Modifiers.Left_Alt,
53     RightAlt = Modifiers.Right_Alt,
54     CapsLock = Modifiers.CapsLock,
55     Shift_CapsLock = Modifiers.Left_Shift | Modifiers.CapsLock,
56     Alt_CapsLock = Modifiers.Left_Alt | Modifiers.CapsLock,
57     Both_Shift = Modifiers.Left_Shift | Modifiers.Right_Shift,
58     Both_Alt = Modifiers.Left_Alt | Modifiers.Right_Alt,
59     Both_Ctrl = Modifiers.Left_Ctrl | Modifiers.Right_Ctrl,
60     RightCtrl_RightShift = Modifiers.Right_Ctrl | Modifiers.Right_Shift,
61     Alt_Ctrl = Modifiers.Left_Alt | Modifiers.Left_Ctrl,
62     Alt_Shift = Modifiers.Left_Alt | Modifiers.Left_Shift,
63     Alt_Space = Modifiers.Left_Alt | Modifiers.Space,
64     Menu = Modifiers.Menu,
65     LeftWin = Modifiers.Left_Win,
66     Win_Space = Modifiers.Left_Win | Modifiers.Space,
67     RightWin = Modifiers.Right_Win,
68     LeftShift = Modifiers.Left_Shift,
69     RightShift = Modifiers.Right_Shift,
70     LeftCtrl = Modifiers.Left_Ctrl,
71     RightCtrl = Modifiers.Right_Ctrl,
72     ScrollLock = Modifiers.ScrollLock
73 }
74 
75 struct ButtonPos
76 {
77     ushort i;
78     ushort pos;
79 }
80 
81 class KeyBar_Buttons
82 {
83     private
84     SDL_Renderer *renderer;
85 
86     bool input_mode;
87 
88     string[] layout_names;
89     Layout[string] layouts;
90     Layout*[] layout_modes;
91     ssize_t mode;
92     LayoutChanger changer;
93     long last_change;
94     long last_shift;
95 
96     string[][3] *letters;
97 
98     KeyHandler[int] handlers;
99     KeyHandler[int] handlers_down;
100     KeyHandler[int] handlers_double;
101     SDL_Scancode[][3] *scans_cur;
102     SDL_Scancode[][3] scans;
103     SDL_Scancode[][3] scans_altgr;
104     ButtonPos[SDL_Scancode] buttonpos_by_scan;
105     ButtonPos[SDL_Scancode] buttonpos_by_scan_altgr;
106 
107     string[] layout_changer_names;
108     LayoutChanger[] layout_changer_values;
109 
110     bool keybar_settings_needed;
111 
112     SDL_Rect[] buttons;
113     ssize_t pos;
114     this(GlobalState gs, SDL_Renderer *renderer, string start_cwd)
115     {
116         this.renderer = renderer;
117         buttons = 
118             [SDL_Rect(0,0,32,32), SDL_Rect(32,0,32,32), SDL_Rect(64,0,32,32), SDL_Rect(96,0,32,32), SDL_Rect(128,0,32,32), SDL_Rect(160,0,32,32), 
119              SDL_Rect(0,32,40,32), SDL_Rect(40,32,32,32), SDL_Rect(72,32,32,32), SDL_Rect(104,32,32,32), SDL_Rect(136,32,32,32), SDL_Rect(168,32,24,32),
120              SDL_Rect(0,64,48,32), SDL_Rect(48,64,32,32), SDL_Rect(80,64,32,32), SDL_Rect(112,64,32,32), SDL_Rect(144,64,32,32), SDL_Rect(176,64,16,32),
121              SDL_Rect(0,96,32,32), SDL_Rect(32,96,32,32), SDL_Rect(64,96,32,32), SDL_Rect(96,96,32,32), SDL_Rect(128,96,32,32), SDL_Rect(160,96,32,32),
122              SDL_Rect(0,128,48,16), SDL_Rect(48,128,32,16), SDL_Rect(80,128,32,16), SDL_Rect(112,128,32,16), SDL_Rect(144,128,48,16)];
123 
124         scans[0] = [SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, 
125             SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, 
126             SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, 
127             SDL_SCANCODE_LSHIFT, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V, SDL_SCANCODE_B, 
128             SDL_SCANCODE_LCTRL, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_SPACE
129         ];
130 
131         scans[1] = [SDL_SCANCODE_EQUALS, SDL_SCANCODE_MINUS, SDL_SCANCODE_0, SDL_SCANCODE_9, SDL_SCANCODE_8, SDL_SCANCODE_7, 
132             SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_P, SDL_SCANCODE_O, SDL_SCANCODE_I, SDL_SCANCODE_U, 
133             SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_SEMICOLON, SDL_SCANCODE_L, SDL_SCANCODE_K, SDL_SCANCODE_J, 0, 
134             SDL_SCANCODE_RSHIFT, SDL_SCANCODE_SLASH, SDL_SCANCODE_PERIOD, SDL_SCANCODE_COMMA, SDL_SCANCODE_M, SDL_SCANCODE_N, 
135             SDL_SCANCODE_RCTRL, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI, 0, 0
136         ];
137 
138         scans[2] = [SDL_SCANCODE_ESCAPE, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_INSERT, SDL_SCANCODE_HOME, SDL_SCANCODE_PAGEUP, 
139             SDL_SCANCODE_GRAVE, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_UP, SDL_SCANCODE_DELETE, SDL_SCANCODE_END, SDL_SCANCODE_PAGEDOWN, 
140             SDL_SCANCODE_TAB, SDL_SCANCODE_LEFT, SDL_SCANCODE_DOWN, SDL_SCANCODE_RIGHT, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_PLUS, 
141             SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_PRINTSCREEN, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_PAUSE, 
142             0, 0, 0, 0, SDL_SCANCODE_RETURN
143         ];
144 
145         scans_altgr[0] = [SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, 
146             SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, 
147             SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, 
148             SDL_SCANCODE_LSHIFT, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V, SDL_SCANCODE_B, 
149             SDL_SCANCODE_LCTRL, SDL_SCANCODE_LALT, SDL_SCANCODE_LGUI, SDL_SCANCODE_MENU, SDL_SCANCODE_SPACE
150         ];
151 
152         scans_altgr[1] = [SDL_SCANCODE_EQUALS, SDL_SCANCODE_MINUS, SDL_SCANCODE_0, SDL_SCANCODE_9, SDL_SCANCODE_8, SDL_SCANCODE_7, 
153             SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_P, SDL_SCANCODE_O, SDL_SCANCODE_I, SDL_SCANCODE_U, 
154             SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_SEMICOLON, SDL_SCANCODE_L, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_H, 
155             SDL_SCANCODE_RSHIFT, SDL_SCANCODE_SLASH, SDL_SCANCODE_PERIOD, SDL_SCANCODE_COMMA, SDL_SCANCODE_M, SDL_SCANCODE_N, 
156             SDL_SCANCODE_RCTRL, SDL_SCANCODE_RALT, SDL_SCANCODE_RGUI, 0, 0
157         ];
158 
159         scans_altgr[2] = [SDL_SCANCODE_KP_1, SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_4, SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, 
160             SDL_SCANCODE_GRAVE, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_KP_7, SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_0, 
161             SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5, SDL_SCANCODE_F6, 
162             SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_F11, SDL_SCANCODE_F12, 
163             0, 0, 0, 0, SDL_SCANCODE_KP_PERIOD
164         ];
165 
166         for (ssize_t i=0; i < 3; i++)
167         {
168             for (ssize_t pos=0; pos < scans[i].length; pos++)
169             {
170                 if (scans[i][pos] > 0)
171                     buttonpos_by_scan[scans[i][pos]] = ButtonPos(cast(ushort) i, cast(ushort) pos);
172                 if (scans_altgr[i][pos] > 0)
173                     buttonpos_by_scan_altgr[scans_altgr[i][pos]] = ButtonPos(cast(ushort) i, cast(ushort) pos);
174             }
175         }
176 
177         layout_changer_names = ["Ctrl + Shift", "Left Alt", "Right Alt",
178             "Caps Lock", "Left Shift + Caps Lock", "Left Alt + Caps Lock",
179             "Both Shift", "Both Alt", "Both Ctrl", "Right Ctrl + Right Shift",
180             "Left Alt + Left Ctrl", "Left Alt + Left Shift", "Left Alt + Space",
181             "Menu", "Left Win", "Left Win + Space", "Right Win",
182             "Left Shift", "Right Shift", "Left Ctrl", "Right Ctrl",
183             "Scroll Lock"];
184 
185         with (LayoutChanger)
186         {
187             layout_changer_values = [ Ctrl_Shift, LeftAlt, RightAlt, 
188                 CapsLock, Shift_CapsLock, Alt_CapsLock,
189                 Both_Shift, Both_Alt, Both_Ctrl, RightCtrl_RightShift,
190                 Alt_Ctrl, Alt_Shift, Alt_Space,
191                 Menu, LeftWin, Win_Space, RightWin,
192                 LeftShift, RightShift, LeftCtrl, RightCtrl,
193                 ScrollLock];
194         }
195 
196         assert(layout_changer_names.length == layout_changer_values.length);
197 
198         read_layouts(gs, start_cwd);
199         SDL_StopTextInput();
200     }
201 
202     long last_buttons_cache_use;
203     Texture_Tick[ButtonParms] button_cache;
204 
205     auto
206     get_button_from_cache(SDL_Rect rect, SDL_Color color)
207     {
208         auto butparm = ButtonParms(rect, color);
209         auto tt = butparm in button_cache;
210         if (tt)
211         {
212             tt.tick = SDL_GetTicks();
213             last_buttons_cache_use = SDL_GetTicks();
214         }
215         else
216         {
217             SDL_Surface* surface = SDL_CreateRGBSurface(0,
218                     rect.w,
219                     rect.h,
220                     32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
221 
222             SDL_Rect dst = SDL_Rect(0,0,rect.w,rect.h);
223             SDL_FillRect(surface, &dst, 0xff000000);
224 
225             dst.x += 1;
226             dst.y += 1;
227             dst.w -= 2;
228             dst.h -= 2;
229 
230             SDL_FillRect(surface, &dst,
231                     (color.a<<24) | (color.r<<16) | 
232                     (color.g<<8)  | color.b);
233 
234             auto texture =
235                 SDL_CreateTextureFromSurface(renderer, surface);
236 
237             button_cache[butparm] = Texture_Tick(rect.w, rect.h, [], texture, SDL_GetTicks());
238             last_buttons_cache_use = SDL_GetTicks();
239             tt = butparm in button_cache;
240         }
241 
242         return tt;
243     }
244 
245     void
246     read_layouts(GlobalState gs, string start_cwd)
247     {
248         string layouts_dir = start_cwd~SL~"layouts"~SL;
249         if (!exists(layouts_dir))
250         {
251             layouts_dir = "/usr/share/unde/layouts/";
252             if (!exists(layouts_dir))
253             {
254                 new Exception("Not found .layouts or /usr/share/unde/layouts/");
255             }
256         }
257 
258         foreach(filename; dirEntries(layouts_dir, SpanMode.breadth))
259         {
260             if (filename.isDir) continue;
261 
262             File file = File(filename);
263 
264             Layout layout;
265             ssize_t sl2 = filename.lastIndexOf(SL);
266             ssize_t sl1 = filename[0..sl2].lastIndexOf(SL);
267             layout.short_name = filename[sl1+1..sl2]~"("~filename[sl2+1..$]~")";
268             string[][3] *letters;
269             ssize_t i = 0;
270 
271             foreach(line; file.byLine())
272             {
273                 if (line.startsWith("Name="))
274                     layout.name = line[5..$].idup();
275                 if (line == "Letters:")
276                 {
277                     i = 0;
278                     letters = &layout.letters;
279                 }
280                 if (line == "Letters_Shift:")
281                 {
282                     i = 0;
283                     letters = &layout.letters_shift;
284                 }
285                 if (line == "Letters_Altgr:")
286                 {
287                     i = 0;
288                     letters = &layout.letters_altgr;
289                 }
290                 if (line == "Letters_Shift_Altgr:")
291                 {
292                     i = 0;
293                     letters = &layout.letters_shift_altgr;
294                 }
295                 if (line == "[]")
296                     i++;
297                 if (line[0] == '[' && line [$-1] == ']')
298                 {
299                     string chr;
300                     bool backslash;
301                     int state = 0;
302                     line = line[1..$-1];
303                     foreach (c; line)
304                     {
305                         if (state == 0)
306                         {
307                             if (c == '\"')
308                             {
309                                 state = 1;
310                                 chr = "";
311                             }
312                         }
313                         else if (state == 1)
314                         {
315                             if (c == '\\' && !backslash)
316                             {
317                                 backslash = true;
318                             }
319                             else if (c == '\"' && !backslash)
320                             {
321                                 (*letters)[i] ~= chr;
322                                 state = 0;
323                             }
324                             else
325                             {
326                                 chr ~= c;
327                                 backslash = false;
328                             }
329                         }
330                     }
331                     i++;
332                 }
333             }
334 
335             layout_names ~= layout.name ~ " - " ~ layout.short_name;
336             layouts[layout.short_name] = layout;
337         }
338 
339         sort!("a < b")(layout_names);
340         load_keybar_settings(gs, this);
341     }
342 }
343 
344 void update_letters(GlobalState gs)
345 {
346     if (gs.keybar.input_mode)
347     {
348         if (gs.alt_gr && gs.shift)
349             gs.keybar.letters = &gs.keybar.layout_modes[gs.keybar.mode].letters_shift_altgr;
350         else if (gs.alt_gr)
351             gs.keybar.letters = &gs.keybar.layout_modes[gs.keybar.mode].letters_altgr;
352         else if (gs.shift)
353             gs.keybar.letters = &gs.keybar.layout_modes[gs.keybar.mode].letters_shift;
354         else
355             gs.keybar.letters = &gs.keybar.layout_modes[gs.keybar.mode].letters;
356     }
357     else
358     {
359         if (gs.alt_gr && gs.shift)
360             gs.keybar.letters = &gs.keybar.layouts["us(basic)"].letters_shift_altgr;
361         else if (gs.alt_gr)
362             gs.keybar.letters = &gs.keybar.layouts["us(basic)"].letters_altgr;
363         else if (gs.shift)
364             gs.keybar.letters = &gs.keybar.layouts["us(basic)"].letters_shift;
365         else
366             gs.keybar.letters = &gs.keybar.layouts["us(basic)"].letters;
367     }
368 }
369 
370 void
371 draw_keybar(GlobalState gs)
372 {
373     ushort[] attrs;
374     for (ssize_t z=0; z<16; z++)
375         attrs ~= Attr.Bold | Attr.Color;
376 
377     SDL_Rect full_rect;
378     full_rect.x = gs.screen.w;
379     full_rect.y = gs.screen.h - 32*4-16;
380     full_rect.w = 32*6;
381     full_rect.h = 32*4+16;
382 
383     if (gs.mouse_screen_x > full_rect.x &&
384             gs.mouse_screen_x < full_rect.x + full_rect.w &&
385             gs.mouse_screen_y > full_rect.y &&
386             gs.mouse_screen_y < full_rect.y + full_rect.h)
387     {
388         gs.keybar.pos = get_position_by_chars(
389                 gs.mouse_screen_x - full_rect.x,
390                 gs.mouse_screen_y - full_rect.y, gs.keybar.buttons);
391     }
392     else gs.keybar.pos = -1;
393 
394     foreach(i, but; gs.keybar.buttons)
395     {
396         SDL_Color color = SDL_Color(0xFF, 0xFF, 0xFF, 0xFF);
397         if (gs.keybar.pos == i)
398         {
399             color = SDL_Color(0x80, 0xFF, 0xFF, 0xFF);
400         }
401         auto tt = gs.keybar.get_button_from_cache(but, color);
402         if (!tt && !tt.texture)
403         {
404             throw new Exception("can't create text_surface: "~
405                     SDL_GetError().fromStringz().idup());
406         }
407 
408         SDL_Rect rect;
409         rect.x = but.x + gs.screen.w;
410         rect.y = but.y + gs.screen.h - 32*4-16;
411         rect.w = but.w;
412         rect.h = but.h;
413 
414         int r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect);
415         if (r < 0)
416         {
417             writefln(
418                     "draw_keybar(): error while render copy: %s", 
419                     SDL_GetError().fromStringz() );
420         }
421 
422         int[3] x = [2, 18, 2];
423         int[3] y = [0, 7, 22];
424         int[3] xp = [0, 16, 0];
425         int[3] yp = [0, 8, 16];
426         SDL_Color[3] bright = [SDL_Color(0x00, 0x00, 0x00, 0xFF),
427             SDL_Color(0xFF, 0x00, 0x00, 0xFF),
428             SDL_Color(0x80, 0x80, 0x80, 0xFF)
429         ];
430         SDL_Color[3] unbright = [SDL_Color(0xC0, 0xC0, 0xC0, 0xFF),
431             SDL_Color(0xFF, 0xC0, 0xC0, 0xFF),
432             SDL_Color(0xE0, 0xE0, 0xE0, 0xFF)
433         ];
434 
435         string description = "";
436 
437         update_letters(gs);
438 
439         if (gs.alt_gr)
440             gs.keybar.scans_cur = &gs.keybar.scans_altgr;
441         else
442             gs.keybar.scans_cur = &gs.keybar.scans;
443 
444         for (ssize_t j=0; j < 3; j++)
445         {
446             bool sameleft = false;
447             if (j==1 && (*gs.keybar.letters)[j][i] == (*gs.keybar.letters)[0][i])
448                 sameleft = true;
449             KeyHandler* key_handler = (*gs.keybar.scans_cur)[j][i] in gs.keybar.handlers;
450             KeyHandler* key_handler_down = (*gs.keybar.scans_cur)[j][i] in gs.keybar.handlers_down;
451             KeyHandler* key_handler_double = (*gs.keybar.scans_cur)[j][i] in gs.keybar.handlers_double;
452             if (key_handler || key_handler_down || key_handler_double)
453             {
454                 if (gs.keybar.pos == i && (*gs.keybar.letters)[j][i] > "")
455                 {
456                     if (key_handler && key_handler.description > "")
457                         description ~= (*gs.keybar.letters)[j][i]~" - "~key_handler.description ~ "\n";
458                     if (key_handler_down && key_handler_down.description > "")
459                         description ~= "Hold " ~ (*gs.keybar.letters)[j][i]~" - "~key_handler_down.description ~ "\n";
460                     if (key_handler_double && key_handler_double.description > "")
461                         description ~= "Double " ~ (*gs.keybar.letters)[j][i]~" - "~key_handler_double.description ~ "\n";
462                 }
463 
464                 if (!key_handler && key_handler_down)
465                     key_handler = key_handler_down;
466 
467                 if (!key_handler && key_handler_double)
468                     key_handler = key_handler_double;
469 
470                 if ( key_handler.icon.endsWith(".png") )
471                 {
472                     string image_file = gs.start_cwd~SL~"images"~SL~key_handler.icon;
473                     if (!exists(image_file))
474                     {
475                         image_file = "/usr/share/unde/images/"~key_handler.icon;
476                         if (!exists(image_file))
477                         {
478                             new Exception("Not found .images or /usr/share/unde/images/");
479                         }
480                     }
481 
482                     auto st = get_image_from_cache(gs, image_file);
483 
484                     if (st)
485                     {
486                         SDL_Rect dst;
487                         dst.x = rect.x + xp[j];
488                         dst.y = rect.y + yp[j];
489                         if (j == 2 && i >= 6*4) 
490                         {
491                             dst.x+=24;
492                             dst.y-=16;
493                         }
494                         dst.w = st.w;
495                         dst.h = st.h;
496 
497                         r = SDL_RenderCopy(gs.renderer, st.texture, null, &dst);
498                         if (r < 0)
499                         {
500                             writefln( "draw_keybar() 2: Error while render copy: %s",
501                                     SDL_GetError().fromStringz() );
502                         }
503                     }
504                     else
505                     {
506                         writefln("Can't load %s: %s",
507                                 image_file,
508                                 IMG_GetError().fromStringz());
509                     }
510                 }
511                 else
512                 {
513                     int fontsize = 8;
514                     if (key_handler.icon.walkLength() > 1 && i < 24 ||
515                             j==2 && i == 28)
516                         fontsize = 6;
517                     if (key_handler.icon == "Menu" ||
518                                 key_handler.icon == "Spc")
519                             fontsize = 7;
520                     tt = gs.text_viewer.font.get_line_from_cache(key_handler.icon, 
521                             fontsize, 48, 20, bright[j], attrs);
522                     if (!tt && !tt.texture)
523                     {
524                         throw new Exception("Can't create text_surface: "~
525                                 TTF_GetError().fromStringz().idup());
526                     }
527 
528                     SDL_Rect dst;
529                     dst.x = rect.x + x[j];
530                     if ( j == 1 && i == 11 )
531                         dst.x -= 8;
532                     dst.y = rect.y + y[j];
533                     if ( key_handler.icon.walkLength == 1 && j == 2 )
534                         dst.y -= 7;
535                     if ( (*gs.keybar.letters)[j][i] == "Enter" && i >= 24 )
536                     {
537                         dst.x = rect.x+24;
538                         dst.y = rect.y+3;
539                     }
540                     dst.w = tt.w;
541                     dst.h = tt.h;
542 
543                     r = SDL_RenderCopy(gs.renderer, tt.texture, null, &dst);
544                     if (r < 0)
545                     {
546                         writefln( "draw_keybar() 3: Error while render copy: %s",
547                                 SDL_GetError().fromStringz() );
548                     }
549 
550                 }
551             }
552             else
553             {
554                 if (sameleft) continue;
555                 int fontsize = 8;
556                 if ((*gs.keybar.letters)[j][i].walkLength() > 1 && i < 24 ||
557                         j==2 && i == 28)
558                     fontsize = 6;
559                 if ((*gs.keybar.letters)[j][i] == "Menu" ||
560                         (*gs.keybar.letters)[j][i] == "Spc")
561                     fontsize = 7;
562                 tt = gs.text_viewer.font.get_line_from_cache((*gs.keybar.letters)[j][i], 
563                         fontsize, 48, 20, (gs.keybar.input_mode?bright[j]:unbright[j]), attrs);
564                 if (!tt && !tt.texture)
565                 {
566                     throw new Exception("Can't create text_surface: "~
567                             TTF_GetError().fromStringz().idup());
568                 }
569 
570                 SDL_Rect dst;
571                 dst.x = rect.x + x[j];
572                 if ( j == 1 && i == 11 )
573                     dst.x -= 8;
574                 dst.y = rect.y + y[j];
575                 if ( (*gs.keybar.letters)[j][i].walkLength == 1 && j == 2 )
576                     dst.y -= 7;
577                 if ( (*gs.keybar.letters)[j][i] == "Enter" && i >= 24 )
578                 {
579                     dst.x = rect.x+24;
580                     dst.y = rect.y+3;
581                 }
582                 dst.w = tt.w;
583                 dst.h = tt.h;
584 
585                 r = SDL_RenderCopy(gs.renderer, tt.texture, null, &dst);
586                 if (r < 0)
587                 {
588                     writefln( "draw_keybar() 4: Error while render copy: %s",
589                             SDL_GetError().fromStringz() );
590                 }
591             }
592         }
593 
594         if (description != "")
595         {
596             description = description[0..$-1];
597 
598             int fontsize = 8;
599             int line_height = cast(int)(round(SQRT2^^fontsize)*1.2);
600             tt = gs.text_viewer.font.get_line_from_cache(description, 
601                     fontsize, 32*6, line_height, SDL_Color(0xFF, 0xFF, 0xFF, 0xFF));
602             if (!tt && !tt.texture)
603             {
604                 throw new Exception("Can't create text_surface: "~
605                         TTF_GetError().fromStringz().idup());
606             }
607 
608             /* Render black background */
609             SDL_Rect dst;
610             dst.x = gs.screen.w;
611             dst.y = gs.screen.h - 32*4-16 - tt.h;
612             dst.w = tt.w;
613             dst.h = tt.h;
614 
615             r = SDL_RenderCopy(gs.renderer, gs.texture_black, null, &dst);
616             if (r < 0)
617             {
618                 writefln( "draw_keybar(), 5: Error while render copy: %s",
619                         SDL_GetError().fromStringz() );
620             }
621 
622             /* Render description of buttons */
623             r = SDL_RenderCopy(gs.renderer, tt.texture, null, &dst);
624             if (r < 0)
625             {
626                 writefln( "draw_keybar() 6: Error while render copy: %s",
627                         SDL_GetError().fromStringz() );
628             }
629         }
630     }
631 }
632 
633 void
634 save_keybar_settings(GlobalState gs)
635 {
636     with(gs.keybar)
637     {
638         Dbt key, data;
639         string keybar_settings_str = "keybar_settings";
640         key = keybar_settings_str;
641         string data_str = "";
642 
643         data_str ~= (cast(char*)&changer)[0..changer.sizeof];
644         foreach(layout_mode; layout_modes)
645         {
646             data_str ~= layout_mode.short_name ~ "\0";
647         }
648 
649         data = data_str;
650 
651         auto res = gs.db_marks.put(null, &key, &data);
652         if (res != 0)
653         {
654             throw new Exception("Oh, no, can't to write keybar settings");
655         }
656     }
657 }
658 
659 void
660 load_keybar_settings(GlobalState gs, KeyBar_Buttons keybar)
661 {
662     with(keybar)
663     {
664         Dbt key, data;
665         string keybar_settings_str = "keybar_settings";
666         key = keybar_settings_str;
667 
668         auto res = gs.db_marks.get(null, &key, &data);
669         if (res == 0)
670         {
671             string data_str = data.to!(string);
672             changer = *cast(LayoutChanger*)(data_str[0..changer.sizeof].ptr);
673             data_str = data_str[changer.sizeof..$];
674             ssize_t pos;
675             while ( (pos = data_str.indexOf("\0")) >= 0 )
676             {
677                 layout_modes ~= &layouts[data_str[0..pos]];
678                 data_str = data_str[pos+1..$];
679             }
680         }
681         else
682         {
683             layout_modes ~= &layouts["us(basic)"];
684             version (Posix)
685             {
686                 string lc_messages = setlocale(LC_MESSAGES, null).fromStringz().idup();
687                 writefln("lc_messages=%s", lc_messages);
688                 if (lc_messages == "" || lc_messages == "C")
689                     lc_messages = environment["LANG"];
690                 writefln("lc_messages=%s", lc_messages);
691                 if (lc_messages.length > 3 && lc_messages[0..3] == "ru_")
692                 {
693                     layout_modes ~= &layouts["ru(winkeys)"];
694                 }
695             }
696 	    else
697 	    version (Windows)
698 	    {
699 		auto lang = 0xFF & GetUserDefaultUILanguage();
700 		if (lang == LANG_RUSSIAN)
701 		{
702 		    layout_modes ~= &layouts["ru(winkeys)"];
703 		}
704 	    }
705             changer = LayoutChanger.LeftWin;
706             keybar_settings_needed = true;
707         }
708     }
709 }