├── LICENSE ├── meson.build ├── proto ├── meson.build ├── virtual-keyboard-unstable-v1.xml └── wayfire-shell-unstable-v2.xml ├── src ├── keymap.tpp ├── layouts.tpp ├── main.cpp ├── meson.build ├── osk.hpp ├── shared │ ├── os-compatibility.c │ └── os-compatibility.h ├── util │ └── clara.hpp ├── virtual-keyboard.cpp ├── virtual-keyboard.hpp ├── wayland-window.cpp └── wayland-window.hpp └── wf-osk.desktop /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Wayfire 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'wf-simple-osk', 3 | 'c', 4 | 'cpp', 5 | version: '0.1', 6 | license: 'MIT', 7 | meson_version: '>=0.43.0', 8 | default_options: [ 9 | 'cpp_std=c++17', 10 | 'c_std=c11', 11 | 'warning_level=2', 12 | 'werror=false', 13 | ], 14 | ) 15 | 16 | gtkmm = dependency('gtkmm-3.0') 17 | wayland_client = dependency('wayland-client') 18 | wayland_protos = dependency('wayland-protocols') 19 | gtkls = dependency('gtk-layer-shell-0') 20 | 21 | add_project_link_arguments(['-rdynamic'], language:'cpp') 22 | add_project_arguments(['-Wno-unused-parameter'], language: 'cpp') 23 | 24 | subdir('proto') 25 | subdir('src') 26 | 27 | install_data( 28 | 'wf-osk.desktop', 29 | install_dir: '@0@/share/applications'.format(get_option('prefix')) 30 | ) 31 | -------------------------------------------------------------------------------- /proto/meson.build: -------------------------------------------------------------------------------- 1 | wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') 2 | 3 | wayland_scanner = find_program('wayland-scanner') 4 | 5 | wayland_scanner_code = generator( 6 | wayland_scanner, 7 | output: '@BASENAME@-protocol.c', 8 | arguments: ['private-code', '@INPUT@', '@OUTPUT@'], 9 | ) 10 | 11 | wayland_scanner_client = generator( 12 | wayland_scanner, 13 | output: '@BASENAME@-client-protocol.h', 14 | arguments: ['client-header', '@INPUT@', '@OUTPUT@'], 15 | ) 16 | 17 | client_protocols = [ 18 | ['wayfire-shell-unstable-v2.xml'], 19 | ['virtual-keyboard-unstable-v1.xml'] 20 | ] 21 | 22 | wl_protos_src = [] 23 | wl_protos_headers = [] 24 | 25 | foreach p : client_protocols 26 | xml = join_paths(p) 27 | wl_protos_headers += wayland_scanner_client.process(xml) 28 | wl_protos_src += wayland_scanner_code.process(xml) 29 | endforeach 30 | 31 | lib_wl_protos = static_library('wl_protos', wl_protos_src + wl_protos_headers, 32 | dependencies: [wayland_client]) # for the include directory 33 | 34 | wf_protos = declare_dependency( 35 | link_with: lib_wl_protos, 36 | sources: wl_protos_headers, 37 | ) 38 | -------------------------------------------------------------------------------- /proto/virtual-keyboard-unstable-v1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright © 2008-2011 Kristian Høgsberg 5 | Copyright © 2010-2013 Intel Corporation 6 | Copyright © 2012-2013 Collabora, Ltd. 7 | Copyright © 2018 Purism SPC 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a 10 | copy of this software and associated documentation files (the "Software"), 11 | to deal in the Software without restriction, including without limitation 12 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 | and/or sell copies of the Software, and to permit persons to whom the 14 | Software is furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice (including the next 17 | paragraph) shall be included in all copies or substantial portions of the 18 | Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | 28 | 29 | 30 | 31 | The virtual keyboard provides an application with requests which emulate 32 | the behaviour of a physical keyboard. 33 | 34 | This interface can be used by clients on its own to provide raw input 35 | events, or it can accompany the input method protocol. 36 | 37 | 38 | 39 | 40 | Provide a file descriptor to the compositor which can be 41 | memory-mapped to provide a keyboard mapping description. 42 | 43 | Format carries a value from the keymap_format enumeration. 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | A key was pressed or released. 57 | The time argument is a timestamp with millisecond granularity, with an 58 | undefined base. All requests regarding a single object must share the 59 | same clock. 60 | 61 | Keymap must be set before issuing this request. 62 | 63 | State carries a value from the key_state enumeration. 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Notifies the compositor that the modifier and/or group state has 73 | changed, and it should update state. 74 | 75 | The client should use wl_keyboard.modifiers event to synchronize its 76 | internal state with seat state. 77 | 78 | Keymap must be set before issuing this request. 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | A virtual keyboard manager allows an application to provide keyboard 94 | input events as if they came from a physical keyboard. 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Creates a new virtual keyboard associated to a seat. 104 | 105 | If the compositor enables a keyboard to perform arbitrary actions, it 106 | should present an error when an untrusted client requests a new 107 | keyboard. 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /proto/wayfire-shell-unstable-v2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | This protocol provides additional events and requests for special DE 6 | clients like panels, docks, etc. 7 | 8 | It is meant as an addition for protocols like wlr-layer-shell. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Represents a single output. 27 | Each output is managed independently from the others. 28 | 29 | 30 | 31 | 32 | Emitted when a window gets fullscreened on the given output. In this 33 | mode, windows in the TOP layer are not visible. 34 | 35 | There will be no two consecutive enter_fullscreen calls, i.e. if 36 | fullscreen mode is entered it will be exited before going into this mode 37 | again. 38 | 39 | 40 | 41 | 42 | 43 | Emitted when the output is no longer in fullscreen mode. Each 44 | leave_fullscreen has a corresponding enter_fullscreen before it. 45 | 46 | 47 | 48 | 49 | 50 | Request the compositor to not render the output, so the output usually 51 | is cleared to black color. To enable output rendering again, call 52 | inhibit_output_done. 53 | 54 | 55 | 56 | 57 | 58 | Stop inhibiting the output. This must be called as many times as 59 | inhibit_output was called to actually uninhibit rendering. 60 | 61 | The inhibit/inhibit_done requests can be called multiple times, even 62 | from different apps, so don't assume that a call to inhibit_done would 63 | always mean actually starting the rendering process. 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | A hotspot on the output is an edge or a corner region of the 77 | output where the mouse or touch point has been residing for a given 78 | amount of time. 79 | 80 | The hotspot can be used for example for autohiding panels, where the 81 | panel is shown when the input hovers on the edge of the output for a 82 | specific amount of time. 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | Means that the mouse and/or touch finger was inside the indicated 98 | hotspot for the given amount of time. 99 | 100 | Emitted at most once for each entry of the input inside the hotspot. 101 | 102 | 103 | 104 | 105 | 106 | This event indicates that the mouse or touch point has left the hotspot 107 | area. 108 | 109 | Emitted only once after each enter. 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/keymap.tpp: -------------------------------------------------------------------------------- 1 | static const char keymap[] = "xkb_keymap {\ 2 | xkb_keycodes \"(unnamed)\" {\ 3 | minimum = 8;\ 4 | maximum = 255;\ 5 | = 9;\ 6 | = 10;\ 7 | = 11;\ 8 | = 12;\ 9 | = 13;\ 10 | = 14;\ 11 | = 15;\ 12 | = 16;\ 13 | = 17;\ 14 | = 18;\ 15 | = 19;\ 16 | = 20;\ 17 | = 21;\ 18 | = 22;\ 19 | = 23;\ 20 | = 24;\ 21 | = 25;\ 22 | = 26;\ 23 | = 27;\ 24 | = 28;\ 25 | = 29;\ 26 | = 30;\ 27 | = 31;\ 28 | = 32;\ 29 | = 33;\ 30 | = 34;\ 31 | = 35;\ 32 | = 36;\ 33 | = 37;\ 34 | = 38;\ 35 | = 39;\ 36 | = 40;\ 37 | = 41;\ 38 | = 42;\ 39 | = 43;\ 40 | = 44;\ 41 | = 45;\ 42 | = 46;\ 43 | = 47;\ 44 | = 48;\ 45 | = 49;\ 46 | = 50;\ 47 | = 51;\ 48 | = 52;\ 49 | = 53;\ 50 | = 54;\ 51 | = 55;\ 52 | = 56;\ 53 | = 57;\ 54 | = 58;\ 55 | = 59;\ 56 | = 60;\ 57 | = 61;\ 58 | = 62;\ 59 | = 63;\ 60 | = 64;\ 61 | = 65;\ 62 | = 66;\ 63 | = 67;\ 64 | = 68;\ 65 | = 69;\ 66 | = 70;\ 67 | = 71;\ 68 | = 72;\ 69 | = 73;\ 70 | = 74;\ 71 | = 75;\ 72 | = 76;\ 73 | = 77;\ 74 | = 78;\ 75 | = 79;\ 76 | = 80;\ 77 | = 81;\ 78 | = 82;\ 79 | = 83;\ 80 | = 84;\ 81 | = 85;\ 82 | = 86;\ 83 | = 87;\ 84 | = 88;\ 85 | = 89;\ 86 | = 90;\ 87 | = 91;\ 88 | = 92;\ 89 | = 94;\ 90 | = 95;\ 91 | = 96;\ 92 | = 97;\ 93 | = 98;\ 94 | = 99;\ 95 | = 100;\ 96 | = 101;\ 97 | = 102;\ 98 | = 103;\ 99 | = 104;\ 100 | = 105;\ 101 | = 106;\ 102 | = 107;\ 103 | = 108;\ 104 | = 109;\ 105 | = 110;\ 106 | = 111;\ 107 | = 112;\ 108 | = 113;\ 109 | = 114;\ 110 | = 115;\ 111 | = 116;\ 112 | = 117;\ 113 | = 118;\ 114 | = 119;\ 115 | = 120;\ 116 | = 121;\ 117 | = 122;\ 118 | = 123;\ 119 | = 124;\ 120 | = 125;\ 121 | = 126;\ 122 | = 127;\ 123 | = 128;\ 124 | = 129;\ 125 | = 130;\ 126 | = 131;\ 127 | = 132;\ 128 | = 133;\ 129 | = 134;\ 130 | = 135;\ 131 | = 136;\ 132 | = 137;\ 133 | = 138;\ 134 | = 139;\ 135 | = 140;\ 136 | = 141;\ 137 | = 142;\ 138 | = 143;\ 139 | = 144;\ 140 | = 145;\ 141 | = 146;\ 142 | = 147;\ 143 | = 148;\ 144 | = 149;\ 145 | = 150;\ 146 | = 151;\ 147 | = 152;\ 148 | = 153;\ 149 | = 154;\ 150 | = 155;\ 151 | = 156;\ 152 | = 157;\ 153 | = 158;\ 154 | = 159;\ 155 | = 160;\ 156 | = 161;\ 157 | = 162;\ 158 | = 163;\ 159 | = 164;\ 160 | = 165;\ 161 | = 166;\ 162 | = 167;\ 163 | = 168;\ 164 | = 169;\ 165 | = 170;\ 166 | = 171;\ 167 | = 172;\ 168 | = 173;\ 169 | = 174;\ 170 | = 175;\ 171 | = 176;\ 172 | = 177;\ 173 | = 178;\ 174 | = 179;\ 175 | = 180;\ 176 | = 181;\ 177 | = 182;\ 178 | = 183;\ 179 | = 184;\ 180 | = 185;\ 181 | = 186;\ 182 | = 187;\ 183 | = 188;\ 184 | = 189;\ 185 | = 190;\ 186 | = 191;\ 187 | = 192;\ 188 | = 193;\ 189 | = 194;\ 190 | = 195;\ 191 | = 196;\ 192 | = 197;\ 193 | = 198;\ 194 | = 199;\ 195 | = 200;\ 196 | = 201;\ 197 | = 202;\ 198 | = 203;\ 199 | = 204;\ 200 | = 205;\ 201 | = 206;\ 202 | = 207;\ 203 | = 208;\ 204 | = 209;\ 205 | = 210;\ 206 | = 211;\ 207 | = 212;\ 208 | = 213;\ 209 | = 214;\ 210 | = 215;\ 211 | = 216;\ 212 | = 217;\ 213 | = 218;\ 214 | = 219;\ 215 | = 220;\ 216 | = 221;\ 217 | = 222;\ 218 | = 223;\ 219 | = 224;\ 220 | = 225;\ 221 | = 226;\ 222 | = 227;\ 223 | = 228;\ 224 | = 229;\ 225 | = 230;\ 226 | = 231;\ 227 | = 232;\ 228 | = 233;\ 229 | = 234;\ 230 | = 235;\ 231 | = 236;\ 232 | = 237;\ 233 | = 238;\ 234 | = 239;\ 235 | = 240;\ 236 | = 241;\ 237 | = 242;\ 238 | = 243;\ 239 | = 244;\ 240 | = 245;\ 241 | = 246;\ 242 | = 247;\ 243 | = 248;\ 244 | = 249;\ 245 | = 250;\ 246 | = 251;\ 247 | = 252;\ 248 | = 253;\ 249 | = 254;\ 250 | = 255;\ 251 | indicator 1 = \"Caps Lock\";\ 252 | indicator 2 = \"Num Lock\";\ 253 | indicator 3 = \"Scroll Lock\";\ 254 | indicator 4 = \"Compose\";\ 255 | indicator 5 = \"Kana\";\ 256 | indicator 6 = \"Sleep\";\ 257 | indicator 7 = \"Suspend\";\ 258 | indicator 8 = \"Mute\";\ 259 | indicator 9 = \"Misc\";\ 260 | indicator 10 = \"Mail\";\ 261 | indicator 11 = \"Charging\";\ 262 | indicator 12 = \"Shift Lock\";\ 263 | indicator 13 = \"Group 2\";\ 264 | indicator 14 = \"Mouse Keys\";\ 265 | alias = ;\ 266 | alias = ;\ 267 | alias = ;\ 268 | alias = ;\ 269 | alias = ;\ 270 | alias = ;\ 271 | alias = ;\ 272 | alias = ;\ 273 | alias = ;\ 274 | alias = ;\ 275 | alias = ;\ 276 | alias = ;\ 277 | alias = ;\ 278 | alias = ;\ 279 | alias = ;\ 280 | alias = ;\ 281 | alias = ;\ 282 | alias = ;\ 283 | alias = ;\ 284 | alias = ;\ 285 | alias = ;\ 286 | alias = ;\ 287 | alias = ;\ 288 | alias = ;\ 289 | alias = ;\ 290 | alias = ;\ 291 | alias = ;\ 292 | alias = ;\ 293 | alias = ;\ 294 | alias = ;\ 295 | alias = ;\ 296 | alias = ;\ 297 | alias = ;\ 298 | };\ 299 | \ 300 | xkb_types \"(unnamed)\" {\ 301 | virtual_modifiers NumLock,Alt,LevelThree,LAlt,RAlt,RControl,LControl,ScrollLock,LevelFive,AltGr,Meta,Super,Hyper;\ 302 | \ 303 | type \"ONE_LEVEL\" {\ 304 | modifiers= none;\ 305 | level_name[Level1]= \"Any\";\ 306 | };\ 307 | type \"TWO_LEVEL\" {\ 308 | modifiers= Shift;\ 309 | map[Shift]= Level2;\ 310 | level_name[Level1]= \"Base\";\ 311 | level_name[Level2]= \"Shift\";\ 312 | };\ 313 | type \"ALPHABETIC\" {\ 314 | modifiers= Shift+Lock;\ 315 | map[Shift]= Level2;\ 316 | map[Lock]= Level2;\ 317 | level_name[Level1]= \"Base\";\ 318 | level_name[Level2]= \"Caps\";\ 319 | };\ 320 | type \"SHIFT+ALT\" {\ 321 | modifiers= Shift+Alt;\ 322 | map[Shift+Alt]= Level2;\ 323 | level_name[Level1]= \"Base\";\ 324 | level_name[Level2]= \"Shift+Alt\";\ 325 | };\ 326 | type \"PC_SUPER_LEVEL2\" {\ 327 | modifiers= Mod4;\ 328 | map[Mod4]= Level2;\ 329 | level_name[Level1]= \"Base\";\ 330 | level_name[Level2]= \"Super\";\ 331 | };\ 332 | type \"PC_CONTROL_LEVEL2\" {\ 333 | modifiers= Control;\ 334 | map[Control]= Level2;\ 335 | level_name[Level1]= \"Base\";\ 336 | level_name[Level2]= \"Control\";\ 337 | };\ 338 | type \"PC_LCONTROL_LEVEL2\" {\ 339 | modifiers= LControl;\ 340 | map[LControl]= Level2;\ 341 | level_name[Level1]= \"Base\";\ 342 | level_name[Level2]= \"LControl\";\ 343 | };\ 344 | type \"PC_RCONTROL_LEVEL2\" {\ 345 | modifiers= RControl;\ 346 | map[RControl]= Level2;\ 347 | level_name[Level1]= \"Base\";\ 348 | level_name[Level2]= \"RControl\";\ 349 | };\ 350 | type \"PC_ALT_LEVEL2\" {\ 351 | modifiers= Alt;\ 352 | map[Alt]= Level2;\ 353 | level_name[Level1]= \"Base\";\ 354 | level_name[Level2]= \"Alt\";\ 355 | };\ 356 | type \"PC_LALT_LEVEL2\" {\ 357 | modifiers= LAlt;\ 358 | map[LAlt]= Level2;\ 359 | level_name[Level1]= \"Base\";\ 360 | level_name[Level2]= \"LAlt\";\ 361 | };\ 362 | type \"PC_RALT_LEVEL2\" {\ 363 | modifiers= RAlt;\ 364 | map[RAlt]= Level2;\ 365 | level_name[Level1]= \"Base\";\ 366 | level_name[Level2]= \"RAlt\";\ 367 | };\ 368 | type \"CTRL+ALT\" {\ 369 | modifiers= Shift+Control+Alt+LevelThree;\ 370 | map[Shift]= Level2;\ 371 | preserve[Shift]= Shift;\ 372 | map[LevelThree]= Level3;\ 373 | map[Shift+LevelThree]= Level4;\ 374 | preserve[Shift+LevelThree]= Shift;\ 375 | map[Control+Alt]= Level5;\ 376 | level_name[Level1]= \"Base\";\ 377 | level_name[Level2]= \"Shift\";\ 378 | level_name[Level3]= \"Alt Base\";\ 379 | level_name[Level4]= \"Shift Alt\";\ 380 | level_name[Level5]= \"Ctrl+Alt\";\ 381 | };\ 382 | type \"LOCAL_EIGHT_LEVEL\" {\ 383 | modifiers= Shift+Lock+Control+LevelThree;\ 384 | map[Shift]= Level2;\ 385 | map[Lock]= Level2;\ 386 | map[LevelThree]= Level3;\ 387 | map[Shift+Lock+LevelThree]= Level3;\ 388 | map[Shift+LevelThree]= Level4;\ 389 | map[Lock+LevelThree]= Level4;\ 390 | map[Control]= Level5;\ 391 | map[Shift+Lock+Control]= Level5;\ 392 | map[Shift+Control]= Level6;\ 393 | map[Lock+Control]= Level6;\ 394 | map[Control+LevelThree]= Level7;\ 395 | map[Shift+Lock+Control+LevelThree]= Level7;\ 396 | map[Shift+Control+LevelThree]= Level8;\ 397 | map[Lock+Control+LevelThree]= Level8;\ 398 | level_name[Level1]= \"Base\";\ 399 | level_name[Level2]= \"Shift\";\ 400 | level_name[Level3]= \"Level3\";\ 401 | level_name[Level4]= \"Shift Level3\";\ 402 | level_name[Level5]= \"Ctrl\";\ 403 | level_name[Level6]= \"Shift Ctrl\";\ 404 | level_name[Level7]= \"Level3 Ctrl\";\ 405 | level_name[Level8]= \"Shift Level3 Ctrl\";\ 406 | };\ 407 | type \"THREE_LEVEL\" {\ 408 | modifiers= Shift+LevelThree;\ 409 | map[Shift]= Level2;\ 410 | map[LevelThree]= Level3;\ 411 | map[Shift+LevelThree]= Level3;\ 412 | level_name[Level1]= \"Base\";\ 413 | level_name[Level2]= \"Shift\";\ 414 | level_name[Level3]= \"Level3\";\ 415 | };\ 416 | type \"EIGHT_LEVEL\" {\ 417 | modifiers= Shift+LevelThree+LevelFive;\ 418 | map[Shift]= Level2;\ 419 | map[LevelThree]= Level3;\ 420 | map[Shift+LevelThree]= Level4;\ 421 | map[LevelFive]= Level5;\ 422 | map[Shift+LevelFive]= Level6;\ 423 | map[LevelThree+LevelFive]= Level7;\ 424 | map[Shift+LevelThree+LevelFive]= Level8;\ 425 | level_name[Level1]= \"Base\";\ 426 | level_name[Level2]= \"Shift\";\ 427 | level_name[Level3]= \"Alt Base\";\ 428 | level_name[Level4]= \"Shift Alt\";\ 429 | level_name[Level5]= \"X\";\ 430 | level_name[Level6]= \"X Shift\";\ 431 | level_name[Level7]= \"X Alt Base\";\ 432 | level_name[Level8]= \"X Shift Alt\";\ 433 | };\ 434 | type \"EIGHT_LEVEL_ALPHABETIC\" {\ 435 | modifiers= Shift+Lock+LevelThree+LevelFive;\ 436 | map[Shift]= Level2;\ 437 | map[Lock]= Level2;\ 438 | map[LevelThree]= Level3;\ 439 | map[Shift+LevelThree]= Level4;\ 440 | map[Lock+LevelThree]= Level4;\ 441 | map[Shift+Lock+LevelThree]= Level3;\ 442 | map[LevelFive]= Level5;\ 443 | map[Shift+LevelFive]= Level6;\ 444 | map[Lock+LevelFive]= Level6;\ 445 | map[LevelThree+LevelFive]= Level7;\ 446 | map[Shift+LevelThree+LevelFive]= Level8;\ 447 | map[Lock+LevelThree+LevelFive]= Level8;\ 448 | map[Shift+Lock+LevelThree+LevelFive]= Level7;\ 449 | level_name[Level1]= \"Base\";\ 450 | level_name[Level2]= \"Shift\";\ 451 | level_name[Level3]= \"Alt Base\";\ 452 | level_name[Level4]= \"Shift Alt\";\ 453 | level_name[Level5]= \"X\";\ 454 | level_name[Level6]= \"X Shift\";\ 455 | level_name[Level7]= \"X Alt Base\";\ 456 | level_name[Level8]= \"X Shift Alt\";\ 457 | };\ 458 | type \"EIGHT_LEVEL_LEVEL_FIVE_LOCK\" {\ 459 | modifiers= Shift+Lock+NumLock+LevelThree+LevelFive;\ 460 | map[Shift]= Level2;\ 461 | map[LevelThree]= Level3;\ 462 | map[Shift+LevelThree]= Level4;\ 463 | map[LevelFive]= Level5;\ 464 | map[Shift+LevelFive]= Level6;\ 465 | preserve[Shift+LevelFive]= Shift;\ 466 | map[LevelThree+LevelFive]= Level7;\ 467 | map[Shift+LevelThree+LevelFive]= Level8;\ 468 | map[NumLock]= Level5;\ 469 | map[Shift+NumLock]= Level6;\ 470 | preserve[Shift+NumLock]= Shift;\ 471 | map[NumLock+LevelThree]= Level7;\ 472 | map[Shift+NumLock+LevelThree]= Level8;\ 473 | map[Shift+NumLock+LevelFive]= Level2;\ 474 | map[NumLock+LevelThree+LevelFive]= Level3;\ 475 | map[Shift+NumLock+LevelThree+LevelFive]= Level4;\ 476 | map[Shift+Lock]= Level2;\ 477 | map[Lock+LevelThree]= Level3;\ 478 | map[Shift+Lock+LevelThree]= Level4;\ 479 | map[Lock+LevelFive]= Level5;\ 480 | map[Shift+Lock+LevelFive]= Level6;\ 481 | preserve[Shift+Lock+LevelFive]= Shift;\ 482 | map[Lock+LevelThree+LevelFive]= Level7;\ 483 | map[Shift+Lock+LevelThree+LevelFive]= Level8;\ 484 | map[Lock+NumLock]= Level5;\ 485 | map[Shift+Lock+NumLock]= Level6;\ 486 | preserve[Shift+Lock+NumLock]= Shift;\ 487 | map[Lock+NumLock+LevelThree]= Level7;\ 488 | map[Shift+Lock+NumLock+LevelThree]= Level8;\ 489 | map[Shift+Lock+NumLock+LevelFive]= Level2;\ 490 | map[Lock+NumLock+LevelThree+LevelFive]= Level3;\ 491 | map[Shift+Lock+NumLock+LevelThree+LevelFive]= Level4;\ 492 | level_name[Level1]= \"Base\";\ 493 | level_name[Level2]= \"Shift\";\ 494 | level_name[Level3]= \"Alt Base\";\ 495 | level_name[Level4]= \"Shift Alt\";\ 496 | level_name[Level5]= \"X\";\ 497 | level_name[Level6]= \"X Shift\";\ 498 | level_name[Level7]= \"X Alt Base\";\ 499 | level_name[Level8]= \"X Shift Alt\";\ 500 | };\ 501 | type \"EIGHT_LEVEL_ALPHABETIC_LEVEL_FIVE_LOCK\" {\ 502 | modifiers= Shift+Lock+NumLock+LevelThree+LevelFive;\ 503 | map[Shift]= Level2;\ 504 | map[LevelThree]= Level3;\ 505 | map[Shift+LevelThree]= Level4;\ 506 | map[LevelFive]= Level5;\ 507 | map[Shift+LevelFive]= Level6;\ 508 | preserve[Shift+LevelFive]= Shift;\ 509 | map[LevelThree+LevelFive]= Level7;\ 510 | map[Shift+LevelThree+LevelFive]= Level8;\ 511 | map[NumLock]= Level5;\ 512 | map[Shift+NumLock]= Level6;\ 513 | preserve[Shift+NumLock]= Shift;\ 514 | map[NumLock+LevelThree]= Level7;\ 515 | map[Shift+NumLock+LevelThree]= Level8;\ 516 | map[Shift+NumLock+LevelFive]= Level2;\ 517 | map[NumLock+LevelThree+LevelFive]= Level3;\ 518 | map[Shift+NumLock+LevelThree+LevelFive]= Level4;\ 519 | map[Lock]= Level2;\ 520 | map[Lock+LevelThree]= Level3;\ 521 | map[Shift+Lock+LevelThree]= Level4;\ 522 | map[Lock+LevelFive]= Level5;\ 523 | map[Shift+Lock+LevelFive]= Level6;\ 524 | map[Lock+LevelThree+LevelFive]= Level7;\ 525 | map[Shift+Lock+LevelThree+LevelFive]= Level8;\ 526 | map[Lock+NumLock]= Level5;\ 527 | map[Shift+Lock+NumLock]= Level6;\ 528 | map[Lock+NumLock+LevelThree]= Level7;\ 529 | map[Shift+Lock+NumLock+LevelThree]= Level8;\ 530 | map[Lock+NumLock+LevelFive]= Level2;\ 531 | map[Lock+NumLock+LevelThree+LevelFive]= Level4;\ 532 | map[Shift+Lock+NumLock+LevelThree+LevelFive]= Level3;\ 533 | level_name[Level1]= \"Base\";\ 534 | level_name[Level2]= \"Shift\";\ 535 | level_name[Level3]= \"Alt Base\";\ 536 | level_name[Level4]= \"Shift Alt\";\ 537 | level_name[Level5]= \"X\";\ 538 | level_name[Level6]= \"X Shift\";\ 539 | level_name[Level7]= \"X Alt Base\";\ 540 | level_name[Level8]= \"X Shift Alt\";\ 541 | };\ 542 | type \"EIGHT_LEVEL_SEMIALPHABETIC\" {\ 543 | modifiers= Shift+Lock+LevelThree+LevelFive;\ 544 | map[Shift]= Level2;\ 545 | map[Lock]= Level2;\ 546 | map[LevelThree]= Level3;\ 547 | map[Shift+LevelThree]= Level4;\ 548 | map[Lock+LevelThree]= Level3;\ 549 | preserve[Lock+LevelThree]= Lock;\ 550 | map[Shift+Lock+LevelThree]= Level4;\ 551 | preserve[Shift+Lock+LevelThree]= Lock;\ 552 | map[LevelFive]= Level5;\ 553 | map[Shift+LevelFive]= Level6;\ 554 | map[Lock+LevelFive]= Level6;\ 555 | preserve[Lock+LevelFive]= Lock;\ 556 | map[Shift+Lock+LevelFive]= Level6;\ 557 | preserve[Shift+Lock+LevelFive]= Lock;\ 558 | map[LevelThree+LevelFive]= Level7;\ 559 | map[Shift+LevelThree+LevelFive]= Level8;\ 560 | map[Lock+LevelThree+LevelFive]= Level7;\ 561 | preserve[Lock+LevelThree+LevelFive]= Lock;\ 562 | map[Shift+Lock+LevelThree+LevelFive]= Level8;\ 563 | preserve[Shift+Lock+LevelThree+LevelFive]= Lock;\ 564 | level_name[Level1]= \"Base\";\ 565 | level_name[Level2]= \"Shift\";\ 566 | level_name[Level3]= \"Alt Base\";\ 567 | level_name[Level4]= \"Shift Alt\";\ 568 | level_name[Level5]= \"X\";\ 569 | level_name[Level6]= \"X Shift\";\ 570 | level_name[Level7]= \"X Alt Base\";\ 571 | level_name[Level8]= \"X Shift Alt\";\ 572 | };\ 573 | type \"FOUR_LEVEL\" {\ 574 | modifiers= Shift+LevelThree;\ 575 | map[Shift]= Level2;\ 576 | map[LevelThree]= Level3;\ 577 | map[Shift+LevelThree]= Level4;\ 578 | level_name[Level1]= \"Base\";\ 579 | level_name[Level2]= \"Shift\";\ 580 | level_name[Level3]= \"Alt Base\";\ 581 | level_name[Level4]= \"Shift Alt\";\ 582 | };\ 583 | type \"FOUR_LEVEL_ALPHABETIC\" {\ 584 | modifiers= Shift+Lock+LevelThree;\ 585 | map[Shift]= Level2;\ 586 | map[Lock]= Level2;\ 587 | map[LevelThree]= Level3;\ 588 | map[Shift+LevelThree]= Level4;\ 589 | map[Lock+LevelThree]= Level4;\ 590 | map[Shift+Lock+LevelThree]= Level3;\ 591 | level_name[Level1]= \"Base\";\ 592 | level_name[Level2]= \"Shift\";\ 593 | level_name[Level3]= \"Alt Base\";\ 594 | level_name[Level4]= \"Shift Alt\";\ 595 | };\ 596 | type \"FOUR_LEVEL_SEMIALPHABETIC\" {\ 597 | modifiers= Shift+Lock+LevelThree;\ 598 | map[Shift]= Level2;\ 599 | map[Lock]= Level2;\ 600 | map[LevelThree]= Level3;\ 601 | map[Shift+LevelThree]= Level4;\ 602 | map[Lock+LevelThree]= Level3;\ 603 | preserve[Lock+LevelThree]= Lock;\ 604 | map[Shift+Lock+LevelThree]= Level4;\ 605 | preserve[Shift+Lock+LevelThree]= Lock;\ 606 | level_name[Level1]= \"Base\";\ 607 | level_name[Level2]= \"Shift\";\ 608 | level_name[Level3]= \"Alt Base\";\ 609 | level_name[Level4]= \"Shift Alt\";\ 610 | };\ 611 | type \"FOUR_LEVEL_MIXED_KEYPAD\" {\ 612 | modifiers= Shift+NumLock+LevelThree;\ 613 | map[NumLock]= Level2;\ 614 | map[Shift]= Level2;\ 615 | map[LevelThree]= Level3;\ 616 | map[NumLock+LevelThree]= Level3;\ 617 | map[Shift+LevelThree]= Level4;\ 618 | map[Shift+NumLock+LevelThree]= Level4;\ 619 | level_name[Level1]= \"Base\";\ 620 | level_name[Level2]= \"Number\";\ 621 | level_name[Level3]= \"Alt Base\";\ 622 | level_name[Level4]= \"Shift Alt\";\ 623 | };\ 624 | type \"FOUR_LEVEL_X\" {\ 625 | modifiers= Shift+Control+Alt+LevelThree;\ 626 | map[LevelThree]= Level2;\ 627 | map[Shift+LevelThree]= Level3;\ 628 | map[Control+Alt]= Level4;\ 629 | level_name[Level1]= \"Base\";\ 630 | level_name[Level2]= \"Alt Base\";\ 631 | level_name[Level3]= \"Shift Alt\";\ 632 | level_name[Level4]= \"Ctrl+Alt\";\ 633 | };\ 634 | type \"SEPARATE_CAPS_AND_SHIFT_ALPHABETIC\" {\ 635 | modifiers= Shift+Lock+LevelThree;\ 636 | map[Shift]= Level2;\ 637 | map[Lock]= Level4;\ 638 | preserve[Lock]= Lock;\ 639 | map[LevelThree]= Level3;\ 640 | map[Shift+LevelThree]= Level4;\ 641 | map[Lock+LevelThree]= Level3;\ 642 | preserve[Lock+LevelThree]= Lock;\ 643 | map[Shift+Lock+LevelThree]= Level3;\ 644 | level_name[Level1]= \"Base\";\ 645 | level_name[Level2]= \"Shift\";\ 646 | level_name[Level3]= \"AltGr Base\";\ 647 | level_name[Level4]= \"Shift AltGr\";\ 648 | };\ 649 | type \"FOUR_LEVEL_PLUS_LOCK\" {\ 650 | modifiers= Shift+Lock+LevelThree;\ 651 | map[Shift]= Level2;\ 652 | map[LevelThree]= Level3;\ 653 | map[Shift+LevelThree]= Level4;\ 654 | map[Lock]= Level5;\ 655 | map[Shift+Lock]= Level2;\ 656 | map[Lock+LevelThree]= Level3;\ 657 | map[Shift+Lock+LevelThree]= Level4;\ 658 | level_name[Level1]= \"Base\";\ 659 | level_name[Level2]= \"Shift\";\ 660 | level_name[Level3]= \"Alt Base\";\ 661 | level_name[Level4]= \"Shift Alt\";\ 662 | level_name[Level5]= \"Lock\";\ 663 | };\ 664 | type \"KEYPAD\" {\ 665 | modifiers= Shift+NumLock;\ 666 | map[Shift]= Level2;\ 667 | map[NumLock]= Level2;\ 668 | level_name[Level1]= \"Base\";\ 669 | level_name[Level2]= \"Number\";\ 670 | };\ 671 | type \"FOUR_LEVEL_KEYPAD\" {\ 672 | modifiers= Shift+NumLock+LevelThree;\ 673 | map[Shift]= Level2;\ 674 | map[NumLock]= Level2;\ 675 | map[LevelThree]= Level3;\ 676 | map[Shift+LevelThree]= Level4;\ 677 | map[NumLock+LevelThree]= Level4;\ 678 | map[Shift+NumLock+LevelThree]= Level3;\ 679 | level_name[Level1]= \"Base\";\ 680 | level_name[Level2]= \"Number\";\ 681 | level_name[Level3]= \"Alt Base\";\ 682 | level_name[Level4]= \"Alt Number\";\ 683 | };\ 684 | };\ 685 | \ 686 | xkb_compatibility \"(unnamed)\" {\ 687 | virtual_modifiers NumLock,Alt,LevelThree,LAlt,RAlt,RControl,LControl,ScrollLock,LevelFive,AltGr,Meta,Super,Hyper;\ 688 | \ 689 | interpret.useModMapMods= AnyLevel;\ 690 | interpret.repeat= False;\ 691 | interpret ISO_Level2_Latch+Exactly(Shift) {\ 692 | useModMapMods=level1;\ 693 | action= LatchMods(modifiers=Shift,clearLocks,latchToLock);\ 694 | };\ 695 | interpret Shift_Lock+AnyOf(Shift+Lock) {\ 696 | action= LockMods(modifiers=Shift);\ 697 | };\ 698 | interpret Num_Lock+AnyOf(all) {\ 699 | virtualModifier= NumLock;\ 700 | action= LockMods(modifiers=NumLock);\ 701 | };\ 702 | interpret ISO_Level3_Shift+AnyOf(all) {\ 703 | virtualModifier= LevelThree;\ 704 | useModMapMods=level1;\ 705 | action= SetMods(modifiers=LevelThree,clearLocks);\ 706 | };\ 707 | interpret ISO_Level3_Latch+AnyOf(all) {\ 708 | virtualModifier= LevelThree;\ 709 | useModMapMods=level1;\ 710 | action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock);\ 711 | };\ 712 | interpret ISO_Level3_Lock+AnyOf(all) {\ 713 | virtualModifier= LevelThree;\ 714 | useModMapMods=level1;\ 715 | action= LockMods(modifiers=LevelThree);\ 716 | };\ 717 | interpret Alt_L+AnyOf(all) {\ 718 | virtualModifier= Alt;\ 719 | action= SetMods(modifiers=modMapMods,clearLocks);\ 720 | };\ 721 | interpret Alt_R+AnyOf(all) {\ 722 | virtualModifier= Alt;\ 723 | action= SetMods(modifiers=modMapMods,clearLocks);\ 724 | };\ 725 | interpret Meta_L+AnyOf(all) {\ 726 | virtualModifier= Meta;\ 727 | action= SetMods(modifiers=modMapMods,clearLocks);\ 728 | };\ 729 | interpret Meta_R+AnyOf(all) {\ 730 | virtualModifier= Meta;\ 731 | action= SetMods(modifiers=modMapMods,clearLocks);\ 732 | };\ 733 | interpret Super_L+AnyOf(all) {\ 734 | virtualModifier= Super;\ 735 | action= SetMods(modifiers=modMapMods,clearLocks);\ 736 | };\ 737 | interpret Super_R+AnyOf(all) {\ 738 | virtualModifier= Super;\ 739 | action= SetMods(modifiers=modMapMods,clearLocks);\ 740 | };\ 741 | interpret Hyper_L+AnyOf(all) {\ 742 | virtualModifier= Hyper;\ 743 | action= SetMods(modifiers=modMapMods,clearLocks);\ 744 | };\ 745 | interpret Hyper_R+AnyOf(all) {\ 746 | virtualModifier= Hyper;\ 747 | action= SetMods(modifiers=modMapMods,clearLocks);\ 748 | };\ 749 | interpret Scroll_Lock+AnyOf(all) {\ 750 | virtualModifier= ScrollLock;\ 751 | action= LockMods(modifiers=modMapMods);\ 752 | };\ 753 | interpret ISO_Level5_Shift+AnyOf(all) {\ 754 | virtualModifier= LevelFive;\ 755 | useModMapMods=level1;\ 756 | action= SetMods(modifiers=LevelFive,clearLocks);\ 757 | };\ 758 | interpret ISO_Level5_Latch+AnyOf(all) {\ 759 | virtualModifier= LevelFive;\ 760 | useModMapMods=level1;\ 761 | action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock);\ 762 | };\ 763 | interpret ISO_Level5_Lock+AnyOf(all) {\ 764 | virtualModifier= LevelFive;\ 765 | useModMapMods=level1;\ 766 | action= LockMods(modifiers=LevelFive);\ 767 | };\ 768 | interpret Mode_switch+AnyOfOrNone(all) {\ 769 | virtualModifier= AltGr;\ 770 | useModMapMods=level1;\ 771 | action= SetGroup(group=+1);\ 772 | };\ 773 | interpret ISO_Level3_Shift+AnyOfOrNone(all) {\ 774 | action= SetMods(modifiers=LevelThree,clearLocks);\ 775 | };\ 776 | interpret ISO_Level3_Latch+AnyOfOrNone(all) {\ 777 | action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock);\ 778 | };\ 779 | interpret ISO_Level3_Lock+AnyOfOrNone(all) {\ 780 | action= LockMods(modifiers=LevelThree);\ 781 | };\ 782 | interpret ISO_Group_Latch+AnyOfOrNone(all) {\ 783 | virtualModifier= AltGr;\ 784 | useModMapMods=level1;\ 785 | action= LatchGroup(group=2);\ 786 | };\ 787 | interpret ISO_Next_Group+AnyOfOrNone(all) {\ 788 | virtualModifier= AltGr;\ 789 | useModMapMods=level1;\ 790 | action= LockGroup(group=+1);\ 791 | };\ 792 | interpret ISO_Prev_Group+AnyOfOrNone(all) {\ 793 | virtualModifier= AltGr;\ 794 | useModMapMods=level1;\ 795 | action= LockGroup(group=-1);\ 796 | };\ 797 | interpret ISO_First_Group+AnyOfOrNone(all) {\ 798 | action= LockGroup(group=1);\ 799 | };\ 800 | interpret ISO_Last_Group+AnyOfOrNone(all) {\ 801 | action= LockGroup(group=2);\ 802 | };\ 803 | interpret KP_1+AnyOfOrNone(all) {\ 804 | repeat= True;\ 805 | action= MovePtr(x=-1,y=+1);\ 806 | };\ 807 | interpret KP_End+AnyOfOrNone(all) {\ 808 | repeat= True;\ 809 | action= MovePtr(x=-1,y=+1);\ 810 | };\ 811 | interpret KP_2+AnyOfOrNone(all) {\ 812 | repeat= True;\ 813 | action= MovePtr(x=+0,y=+1);\ 814 | };\ 815 | interpret KP_Down+AnyOfOrNone(all) {\ 816 | repeat= True;\ 817 | action= MovePtr(x=+0,y=+1);\ 818 | };\ 819 | interpret KP_3+AnyOfOrNone(all) {\ 820 | repeat= True;\ 821 | action= MovePtr(x=+1,y=+1);\ 822 | };\ 823 | interpret KP_Next+AnyOfOrNone(all) {\ 824 | repeat= True;\ 825 | action= MovePtr(x=+1,y=+1);\ 826 | };\ 827 | interpret KP_4+AnyOfOrNone(all) {\ 828 | repeat= True;\ 829 | action= MovePtr(x=-1,y=+0);\ 830 | };\ 831 | interpret KP_Left+AnyOfOrNone(all) {\ 832 | repeat= True;\ 833 | action= MovePtr(x=-1,y=+0);\ 834 | };\ 835 | interpret KP_6+AnyOfOrNone(all) {\ 836 | repeat= True;\ 837 | action= MovePtr(x=+1,y=+0);\ 838 | };\ 839 | interpret KP_Right+AnyOfOrNone(all) {\ 840 | repeat= True;\ 841 | action= MovePtr(x=+1,y=+0);\ 842 | };\ 843 | interpret KP_7+AnyOfOrNone(all) {\ 844 | repeat= True;\ 845 | action= MovePtr(x=-1,y=-1);\ 846 | };\ 847 | interpret KP_Home+AnyOfOrNone(all) {\ 848 | repeat= True;\ 849 | action= MovePtr(x=-1,y=-1);\ 850 | };\ 851 | interpret KP_8+AnyOfOrNone(all) {\ 852 | repeat= True;\ 853 | action= MovePtr(x=+0,y=-1);\ 854 | };\ 855 | interpret KP_Up+AnyOfOrNone(all) {\ 856 | repeat= True;\ 857 | action= MovePtr(x=+0,y=-1);\ 858 | };\ 859 | interpret KP_9+AnyOfOrNone(all) {\ 860 | repeat= True;\ 861 | action= MovePtr(x=+1,y=-1);\ 862 | };\ 863 | interpret KP_Prior+AnyOfOrNone(all) {\ 864 | repeat= True;\ 865 | action= MovePtr(x=+1,y=-1);\ 866 | };\ 867 | interpret KP_5+AnyOfOrNone(all) {\ 868 | repeat= True;\ 869 | action= PtrBtn(button=default);\ 870 | };\ 871 | interpret KP_Begin+AnyOfOrNone(all) {\ 872 | repeat= True;\ 873 | action= PtrBtn(button=default);\ 874 | };\ 875 | interpret KP_F2+AnyOfOrNone(all) {\ 876 | repeat= True;\ 877 | action= SetPtrDflt(affect=button,button=1);\ 878 | };\ 879 | interpret KP_Divide+AnyOfOrNone(all) {\ 880 | repeat= True;\ 881 | action= SetPtrDflt(affect=button,button=1);\ 882 | };\ 883 | interpret KP_F3+AnyOfOrNone(all) {\ 884 | repeat= True;\ 885 | action= SetPtrDflt(affect=button,button=2);\ 886 | };\ 887 | interpret KP_Multiply+AnyOfOrNone(all) {\ 888 | repeat= True;\ 889 | action= SetPtrDflt(affect=button,button=2);\ 890 | };\ 891 | interpret KP_F4+AnyOfOrNone(all) {\ 892 | repeat= True;\ 893 | action= SetPtrDflt(affect=button,button=3);\ 894 | };\ 895 | interpret KP_Subtract+AnyOfOrNone(all) {\ 896 | repeat= True;\ 897 | action= SetPtrDflt(affect=button,button=3);\ 898 | };\ 899 | interpret KP_Separator+AnyOfOrNone(all) {\ 900 | repeat= True;\ 901 | action= PtrBtn(button=default,count=2);\ 902 | };\ 903 | interpret KP_Add+AnyOfOrNone(all) {\ 904 | repeat= True;\ 905 | action= PtrBtn(button=default,count=2);\ 906 | };\ 907 | interpret KP_0+AnyOfOrNone(all) {\ 908 | repeat= True;\ 909 | action= LockPtrBtn(button=default,affect=lock);\ 910 | };\ 911 | interpret KP_Insert+AnyOfOrNone(all) {\ 912 | repeat= True;\ 913 | action= LockPtrBtn(button=default,affect=lock);\ 914 | };\ 915 | interpret KP_Decimal+AnyOfOrNone(all) {\ 916 | repeat= True;\ 917 | action= LockPtrBtn(button=default,affect=unlock);\ 918 | };\ 919 | interpret KP_Delete+AnyOfOrNone(all) {\ 920 | repeat= True;\ 921 | action= LockPtrBtn(button=default,affect=unlock);\ 922 | };\ 923 | interpret F25+AnyOfOrNone(all) {\ 924 | repeat= True;\ 925 | action= SetPtrDflt(affect=button,button=1);\ 926 | };\ 927 | interpret F26+AnyOfOrNone(all) {\ 928 | repeat= True;\ 929 | action= SetPtrDflt(affect=button,button=2);\ 930 | };\ 931 | interpret F27+AnyOfOrNone(all) {\ 932 | repeat= True;\ 933 | action= MovePtr(x=-1,y=-1);\ 934 | };\ 935 | interpret F29+AnyOfOrNone(all) {\ 936 | repeat= True;\ 937 | action= MovePtr(x=+1,y=-1);\ 938 | };\ 939 | interpret F31+AnyOfOrNone(all) {\ 940 | repeat= True;\ 941 | action= PtrBtn(button=default);\ 942 | };\ 943 | interpret F33+AnyOfOrNone(all) {\ 944 | repeat= True;\ 945 | action= MovePtr(x=-1,y=+1);\ 946 | };\ 947 | interpret F35+AnyOfOrNone(all) {\ 948 | repeat= True;\ 949 | action= MovePtr(x=+1,y=+1);\ 950 | };\ 951 | interpret Pointer_Button_Dflt+AnyOfOrNone(all) {\ 952 | action= PtrBtn(button=default);\ 953 | };\ 954 | interpret Pointer_Button1+AnyOfOrNone(all) {\ 955 | action= PtrBtn(button=1);\ 956 | };\ 957 | interpret Pointer_Button2+AnyOfOrNone(all) {\ 958 | action= PtrBtn(button=2);\ 959 | };\ 960 | interpret Pointer_Button3+AnyOfOrNone(all) {\ 961 | action= PtrBtn(button=3);\ 962 | };\ 963 | interpret Pointer_DblClick_Dflt+AnyOfOrNone(all) {\ 964 | action= PtrBtn(button=default,count=2);\ 965 | };\ 966 | interpret Pointer_DblClick1+AnyOfOrNone(all) {\ 967 | action= PtrBtn(button=1,count=2);\ 968 | };\ 969 | interpret Pointer_DblClick2+AnyOfOrNone(all) {\ 970 | action= PtrBtn(button=2,count=2);\ 971 | };\ 972 | interpret Pointer_DblClick3+AnyOfOrNone(all) {\ 973 | action= PtrBtn(button=3,count=2);\ 974 | };\ 975 | interpret Pointer_Drag_Dflt+AnyOfOrNone(all) {\ 976 | action= LockPtrBtn(button=default);\ 977 | };\ 978 | interpret Pointer_Drag1+AnyOfOrNone(all) {\ 979 | action= LockPtrBtn(button=1);\ 980 | };\ 981 | interpret Pointer_Drag2+AnyOfOrNone(all) {\ 982 | action= LockPtrBtn(button=2);\ 983 | };\ 984 | interpret Pointer_Drag3+AnyOfOrNone(all) {\ 985 | action= LockPtrBtn(button=3);\ 986 | };\ 987 | interpret Pointer_EnableKeys+AnyOfOrNone(all) {\ 988 | action= LockControls(controls=MouseKeys);\ 989 | };\ 990 | interpret Pointer_Accelerate+AnyOfOrNone(all) {\ 991 | action= LockControls(controls=MouseKeysAccel);\ 992 | };\ 993 | interpret Pointer_DfltBtnNext+AnyOfOrNone(all) {\ 994 | action= SetPtrDflt(affect=button,button=+1);\ 995 | };\ 996 | interpret Pointer_DfltBtnPrev+AnyOfOrNone(all) {\ 997 | action= SetPtrDflt(affect=button,button=-1);\ 998 | };\ 999 | interpret AccessX_Enable+AnyOfOrNone(all) {\ 1000 | action= LockControls(controls=AccessXKeys);\ 1001 | };\ 1002 | interpret AccessX_Feedback_Enable+AnyOfOrNone(all) {\ 1003 | action= LockControls(controls=AccessXFeedback);\ 1004 | };\ 1005 | interpret RepeatKeys_Enable+AnyOfOrNone(all) {\ 1006 | action= LockControls(controls=RepeatKeys);\ 1007 | };\ 1008 | interpret SlowKeys_Enable+AnyOfOrNone(all) {\ 1009 | action= LockControls(controls=SlowKeys);\ 1010 | };\ 1011 | interpret BounceKeys_Enable+AnyOfOrNone(all) {\ 1012 | action= LockControls(controls=BounceKeys);\ 1013 | };\ 1014 | interpret StickyKeys_Enable+AnyOfOrNone(all) {\ 1015 | action= LockControls(controls=StickyKeys);\ 1016 | };\ 1017 | interpret MouseKeys_Enable+AnyOfOrNone(all) {\ 1018 | action= LockControls(controls=MouseKeys);\ 1019 | };\ 1020 | interpret MouseKeys_Accel_Enable+AnyOfOrNone(all) {\ 1021 | action= LockControls(controls=MouseKeysAccel);\ 1022 | };\ 1023 | interpret Overlay1_Enable+AnyOfOrNone(all) {\ 1024 | action= LockControls(controls=none);\ 1025 | };\ 1026 | interpret Overlay2_Enable+AnyOfOrNone(all) {\ 1027 | action= LockControls(controls=none);\ 1028 | };\ 1029 | interpret AudibleBell_Enable+AnyOfOrNone(all) {\ 1030 | action= LockControls(controls=AudibleBell);\ 1031 | };\ 1032 | interpret Terminate_Server+AnyOfOrNone(all) {\ 1033 | action= Terminate();\ 1034 | };\ 1035 | interpret Alt_L+AnyOfOrNone(all) {\ 1036 | action= SetMods(modifiers=Alt,clearLocks);\ 1037 | };\ 1038 | interpret Alt_R+AnyOfOrNone(all) {\ 1039 | action= SetMods(modifiers=Alt,clearLocks);\ 1040 | };\ 1041 | interpret Meta_L+AnyOfOrNone(all) {\ 1042 | action= SetMods(modifiers=Meta,clearLocks);\ 1043 | };\ 1044 | interpret Meta_R+AnyOfOrNone(all) {\ 1045 | action= SetMods(modifiers=Meta,clearLocks);\ 1046 | };\ 1047 | interpret Super_L+AnyOfOrNone(all) {\ 1048 | action= SetMods(modifiers=Super,clearLocks);\ 1049 | };\ 1050 | interpret Super_R+AnyOfOrNone(all) {\ 1051 | action= SetMods(modifiers=Super,clearLocks);\ 1052 | };\ 1053 | interpret Hyper_L+AnyOfOrNone(all) {\ 1054 | action= SetMods(modifiers=Hyper,clearLocks);\ 1055 | };\ 1056 | interpret Hyper_R+AnyOfOrNone(all) {\ 1057 | action= SetMods(modifiers=Hyper,clearLocks);\ 1058 | };\ 1059 | interpret Shift_L+AnyOfOrNone(all) {\ 1060 | action= SetMods(modifiers=Shift,clearLocks);\ 1061 | };\ 1062 | interpret XF86Switch_VT_1+AnyOfOrNone(all) {\ 1063 | repeat= True;\ 1064 | action= SwitchScreen(screen=1,!same);\ 1065 | };\ 1066 | interpret XF86Switch_VT_2+AnyOfOrNone(all) {\ 1067 | repeat= True;\ 1068 | action= SwitchScreen(screen=2,!same);\ 1069 | };\ 1070 | interpret XF86Switch_VT_3+AnyOfOrNone(all) {\ 1071 | repeat= True;\ 1072 | action= SwitchScreen(screen=3,!same);\ 1073 | };\ 1074 | interpret XF86Switch_VT_4+AnyOfOrNone(all) {\ 1075 | repeat= True;\ 1076 | action= SwitchScreen(screen=4,!same);\ 1077 | };\ 1078 | interpret XF86Switch_VT_5+AnyOfOrNone(all) {\ 1079 | repeat= True;\ 1080 | action= SwitchScreen(screen=5,!same);\ 1081 | };\ 1082 | interpret XF86Switch_VT_6+AnyOfOrNone(all) {\ 1083 | repeat= True;\ 1084 | action= SwitchScreen(screen=6,!same);\ 1085 | };\ 1086 | interpret XF86Switch_VT_7+AnyOfOrNone(all) {\ 1087 | repeat= True;\ 1088 | action= SwitchScreen(screen=7,!same);\ 1089 | };\ 1090 | interpret XF86Switch_VT_8+AnyOfOrNone(all) {\ 1091 | repeat= True;\ 1092 | action= SwitchScreen(screen=8,!same);\ 1093 | };\ 1094 | interpret XF86Switch_VT_9+AnyOfOrNone(all) {\ 1095 | repeat= True;\ 1096 | action= SwitchScreen(screen=9,!same);\ 1097 | };\ 1098 | interpret XF86Switch_VT_10+AnyOfOrNone(all) {\ 1099 | repeat= True;\ 1100 | action= SwitchScreen(screen=10,!same);\ 1101 | };\ 1102 | interpret XF86Switch_VT_11+AnyOfOrNone(all) {\ 1103 | repeat= True;\ 1104 | action= SwitchScreen(screen=11,!same);\ 1105 | };\ 1106 | interpret XF86Switch_VT_12+AnyOfOrNone(all) {\ 1107 | repeat= True;\ 1108 | action= SwitchScreen(screen=12,!same);\ 1109 | };\ 1110 | interpret XF86LogGrabInfo+AnyOfOrNone(all) {\ 1111 | repeat= True;\ 1112 | action= Private(type=0x86,data[0]=0x50,data[1]=0x72,data[2]=0x47,data[3]=0x72,data[4]=0x62,data[5]=0x73,data[6]=0x00);\ 1113 | };\ 1114 | interpret XF86LogWindowTree+AnyOfOrNone(all) {\ 1115 | repeat= True;\ 1116 | action= Private(type=0x86,data[0]=0x50,data[1]=0x72,data[2]=0x57,data[3]=0x69,data[4]=0x6e,data[5]=0x73,data[6]=0x00);\ 1117 | };\ 1118 | interpret XF86Next_VMode+AnyOfOrNone(all) {\ 1119 | repeat= True;\ 1120 | action= Private(type=0x86,data[0]=0x2b,data[1]=0x56,data[2]=0x4d,data[3]=0x6f,data[4]=0x64,data[5]=0x65,data[6]=0x00);\ 1121 | };\ 1122 | interpret XF86Prev_VMode+AnyOfOrNone(all) {\ 1123 | repeat= True;\ 1124 | action= Private(type=0x86,data[0]=0x2d,data[1]=0x56,data[2]=0x4d,data[3]=0x6f,data[4]=0x64,data[5]=0x65,data[6]=0x00);\ 1125 | };\ 1126 | interpret ISO_Level5_Shift+AnyOfOrNone(all) {\ 1127 | action= SetMods(modifiers=LevelFive,clearLocks);\ 1128 | };\ 1129 | interpret ISO_Level5_Latch+AnyOfOrNone(all) {\ 1130 | action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock);\ 1131 | };\ 1132 | interpret ISO_Level5_Lock+AnyOfOrNone(all) {\ 1133 | action= LockMods(modifiers=LevelFive);\ 1134 | };\ 1135 | interpret Caps_Lock+AnyOfOrNone(all) {\ 1136 | action= LockMods(modifiers=Lock);\ 1137 | };\ 1138 | interpret Any+Exactly(Lock) {\ 1139 | action= LockMods(modifiers=Lock);\ 1140 | };\ 1141 | interpret Any+AnyOf(all) {\ 1142 | action= SetMods(modifiers=modMapMods,clearLocks);\ 1143 | };\ 1144 | indicator \"Caps Lock\" {\ 1145 | whichModState= locked;\ 1146 | modifiers= Lock;\ 1147 | };\ 1148 | indicator \"Num Lock\" {\ 1149 | whichModState= locked;\ 1150 | modifiers= NumLock;\ 1151 | };\ 1152 | indicator \"Scroll Lock\" {\ 1153 | whichModState= locked;\ 1154 | modifiers= ScrollLock;\ 1155 | };\ 1156 | indicator \"Shift Lock\" {\ 1157 | whichModState= locked;\ 1158 | modifiers= Shift;\ 1159 | };\ 1160 | indicator \"Group 2\" {\ 1161 | groups= 0xfe;\ 1162 | };\ 1163 | indicator \"Mouse Keys\" {\ 1164 | controls= MouseKeys;\ 1165 | };\ 1166 | };\ 1167 | \ 1168 | xkb_symbols \"(unnamed)\" {\ 1169 | name[group1]=\"English (US)\";\ 1170 | \ 1171 | key { [ Escape ] };\ 1172 | key { [ 1, exclam ] };\ 1173 | key { [ 2, at ] };\ 1174 | key { [ 3, numbersign ] };\ 1175 | key { [ 4, dollar ] };\ 1176 | key { [ 5, percent ] };\ 1177 | key { [ 6, asciicircum ] };\ 1178 | key { [ 7, ampersand ] };\ 1179 | key { [ 8, asterisk ] };\ 1180 | key { [ 9, parenleft ] };\ 1181 | key { [ 0, parenright ] };\ 1182 | key { [ minus, underscore ] };\ 1183 | key { [ equal, plus ] };\ 1184 | key { [ BackSpace, BackSpace ] };\ 1185 | key { [ Tab, ISO_Left_Tab ] };\ 1186 | key { [ q, Q, 1 ] };\ 1187 | key { [ w, W, 2 ] };\ 1188 | key { [ e, E, 3 ] };\ 1189 | key { [ r, R, 4 ] };\ 1190 | key { [ t, T, 5 ] };\ 1191 | key { [ y, Y, 6 ] };\ 1192 | key { [ u, U, 7 ] };\ 1193 | key { [ i, I, 8 ] };\ 1194 | key { [ o, O, 9 ] };\ 1195 | key { [ p, P, 0 ] };\ 1196 | key { [ bracketleft, braceleft ] };\ 1197 | key { [ bracketright, braceright ] };\ 1198 | key { [ Return ] };\ 1199 | key { [ Control_L ] };\ 1200 | key { [ a, A, minus ] };\ 1201 | key { [ s, S, at ] };\ 1202 | key { [ d, D, asterisk ] };\ 1203 | key { [ f, F, asciicircum ] };\ 1204 | key { [ g, G, colon ] };\ 1205 | key { [ h, H, semicolon ] };\ 1206 | key { [ j, J, parenleft ] };\ 1207 | key { [ k, K, parenright ] };\ 1208 | key { [ l, L, asciitilde ] };\ 1209 | key { [ semicolon, colon ] };\ 1210 | key { [ apostrophe, quotedbl ] };\ 1211 | key { [ grave, asciitilde ] };\ 1212 | key { [ Shift_L ] };\ 1213 | key { [ backslash, bar ] };\ 1214 | key { [ z, Z, slash ] };\ 1215 | key { [ x, X, apostrophe ] };\ 1216 | key { [ c, C, quotedbl ] };\ 1217 | key { [ v, V, plus ] };\ 1218 | key { [ b, B, equal ] };\ 1219 | key { [ n, N, question ] };\ 1220 | key { [ m, M, exclam ] };\ 1221 | key { [ comma, less, backslash] };\ 1222 | key { [ period, greater, bar ] };\ 1223 | key { [ slash, question ] };\ 1224 | key { [ Shift_R ] };\ 1225 | key {\ 1226 | type= \"CTRL+ALT\",\ 1227 | symbols[Group1]= [ KP_Multiply, KP_Multiply, KP_Multiply, KP_Multiply, XF86ClearGrab ]\ 1228 | };\ 1229 | key { [ Alt_L, Meta_L ] };\ 1230 | key { [ space ] };\ 1231 | key { [ Caps_Lock ] };\ 1232 | key {\ 1233 | type= \"CTRL+ALT\",\ 1234 | symbols[Group1]= [ F1, F1, F1, F1, XF86Switch_VT_1 ]\ 1235 | };\ 1236 | key {\ 1237 | type= \"CTRL+ALT\",\ 1238 | symbols[Group1]= [ F2, F2, F2, F2, XF86Switch_VT_2 ]\ 1239 | };\ 1240 | key {\ 1241 | type= \"CTRL+ALT\",\ 1242 | symbols[Group1]= [ F3, F3, F3, F3, XF86Switch_VT_3 ]\ 1243 | };\ 1244 | key {\ 1245 | type= \"CTRL+ALT\",\ 1246 | symbols[Group1]= [ F4, F4, F4, F4, XF86Switch_VT_4 ]\ 1247 | };\ 1248 | key {\ 1249 | type= \"CTRL+ALT\",\ 1250 | symbols[Group1]= [ F5, F5, F5, F5, XF86Switch_VT_5 ]\ 1251 | };\ 1252 | key {\ 1253 | type= \"CTRL+ALT\",\ 1254 | symbols[Group1]= [ F6, F6, F6, F6, XF86Switch_VT_6 ]\ 1255 | };\ 1256 | key {\ 1257 | type= \"CTRL+ALT\",\ 1258 | symbols[Group1]= [ F7, F7, F7, F7, XF86Switch_VT_7 ]\ 1259 | };\ 1260 | key {\ 1261 | type= \"CTRL+ALT\",\ 1262 | symbols[Group1]= [ F8, F8, F8, F8, XF86Switch_VT_8 ]\ 1263 | };\ 1264 | key {\ 1265 | type= \"CTRL+ALT\",\ 1266 | symbols[Group1]= [ F9, F9, F9, F9, XF86Switch_VT_9 ]\ 1267 | };\ 1268 | key {\ 1269 | type= \"CTRL+ALT\",\ 1270 | symbols[Group1]= [ F10, F10, F10, F10, XF86Switch_VT_10 ]\ 1271 | };\ 1272 | key { [ Num_Lock ] };\ 1273 | key { [ Scroll_Lock ] };\ 1274 | key { [ KP_Home, KP_7 ] };\ 1275 | key { [ KP_Up, KP_8 ] };\ 1276 | key { [ KP_Prior, KP_9 ] };\ 1277 | key {\ 1278 | type= \"CTRL+ALT\",\ 1279 | symbols[Group1]= [ KP_Subtract, KP_Subtract, KP_Subtract, KP_Subtract, XF86Prev_VMode ]\ 1280 | };\ 1281 | key { [ KP_Left, KP_4 ] };\ 1282 | key { [ KP_Begin, KP_5 ] };\ 1283 | key { [ KP_Right, KP_6 ] };\ 1284 | key {\ 1285 | type= \"CTRL+ALT\",\ 1286 | symbols[Group1]= [ KP_Add, KP_Add, KP_Add, KP_Add, XF86Next_VMode ]\ 1287 | };\ 1288 | key { [ KP_End, KP_1 ] };\ 1289 | key { [ KP_Down, KP_2 ] };\ 1290 | key { [ KP_Next, KP_3 ] };\ 1291 | key { [ KP_Insert, KP_0 ] };\ 1292 | key { [ KP_Delete, KP_Decimal ] };\ 1293 | key { [ ISO_Level3_Shift ] };\ 1294 | key { [ less, greater, bar, brokenbar ] };\ 1295 | key {\ 1296 | type= \"CTRL+ALT\",\ 1297 | symbols[Group1]= [ F11, F11, F11, F11, XF86Switch_VT_11 ]\ 1298 | };\ 1299 | key {\ 1300 | type= \"CTRL+ALT\",\ 1301 | symbols[Group1]= [ F12, F12, F12, F12, XF86Switch_VT_12 ]\ 1302 | };\ 1303 | key { [ Katakana ] };\ 1304 | key { [ Hiragana ] };\ 1305 | key { [ Henkan_Mode ] };\ 1306 | key { [ Hiragana_Katakana ] };\ 1307 | key { [ Muhenkan ] };\ 1308 | key { [ KP_Enter ] };\ 1309 | key { [ Control_R ] };\ 1310 | key {\ 1311 | type= \"CTRL+ALT\",\ 1312 | symbols[Group1]= [ KP_Divide, KP_Divide, KP_Divide, KP_Divide, XF86Ungrab ]\ 1313 | };\ 1314 | key {\ 1315 | type= \"PC_ALT_LEVEL2\",\ 1316 | symbols[Group1]= [ Print, Sys_Req ]\ 1317 | };\ 1318 | key {\ 1319 | type= \"TWO_LEVEL\",\ 1320 | symbols[Group1]= [ Alt_R, Meta_R ]\ 1321 | };\ 1322 | key { [ Linefeed ] };\ 1323 | key { [ Home ] };\ 1324 | key { [ Up ] };\ 1325 | key { [ Prior ] };\ 1326 | key { [ Left ] };\ 1327 | key { [ Right ] };\ 1328 | key { [ End ] };\ 1329 | key { [ Down ] };\ 1330 | key { [ Next ] };\ 1331 | key { [ Insert ] };\ 1332 | key { [ Delete ] };\ 1333 | key { [ XF86AudioMute ] };\ 1334 | key { [ XF86AudioLowerVolume ] };\ 1335 | key { [ XF86AudioRaiseVolume ] };\ 1336 | key { [ XF86PowerOff ] };\ 1337 | key { [ KP_Equal ] };\ 1338 | key { [ plusminus ] };\ 1339 | key {\ 1340 | type= \"PC_CONTROL_LEVEL2\",\ 1341 | symbols[Group1]= [ Pause, Break ]\ 1342 | };\ 1343 | key { [ XF86LaunchA ] };\ 1344 | key { [ KP_Decimal, KP_Decimal ] };\ 1345 | key { [ Hangul ] };\ 1346 | key { [ Hangul_Hanja ] };\ 1347 | key { [ Super_L ] };\ 1348 | key { [ Super_R ] };\ 1349 | key { [ Menu ] };\ 1350 | key { [ Cancel ] };\ 1351 | key { [ Redo ] };\ 1352 | key { [ SunProps ] };\ 1353 | key { [ Undo ] };\ 1354 | key { [ SunFront ] };\ 1355 | key { [ XF86Copy ] };\ 1356 | key { [ XF86Open ] };\ 1357 | key { [ XF86Paste ] };\ 1358 | key { [ Find ] };\ 1359 | key { [ XF86Cut ] };\ 1360 | key { [ Help ] };\ 1361 | key { [ XF86MenuKB ] };\ 1362 | key { [ XF86Calculator ] };\ 1363 | key { [ XF86Sleep ] };\ 1364 | key { [ XF86WakeUp ] };\ 1365 | key { [ XF86Explorer ] };\ 1366 | key { [ XF86Send ] };\ 1367 | key { [ XF86Xfer ] };\ 1368 | key { [ XF86Launch1 ] };\ 1369 | key { [ XF86Launch2 ] };\ 1370 | key { [ XF86WWW ] };\ 1371 | key { [ XF86DOS ] };\ 1372 | key { [ XF86ScreenSaver ] };\ 1373 | key { [ XF86RotateWindows ] };\ 1374 | key { [ XF86TaskPane ] };\ 1375 | key { [ XF86Mail ] };\ 1376 | key { [ XF86Favorites ] };\ 1377 | key { [ XF86MyComputer ] };\ 1378 | key { [ XF86Back ] };\ 1379 | key { [ XF86Forward ] };\ 1380 | key { [ XF86Eject ] };\ 1381 | key { [ XF86Eject, XF86Eject ] };\ 1382 | key { [ XF86AudioNext ] };\ 1383 | key { [ XF86AudioPlay, XF86AudioPause ] };\ 1384 | key { [ XF86AudioPrev ] };\ 1385 | key { [ XF86AudioStop, XF86Eject ] };\ 1386 | key { [ XF86AudioRecord ] };\ 1387 | key { [ XF86AudioRewind ] };\ 1388 | key { [ XF86Phone ] };\ 1389 | key { [ XF86Tools ] };\ 1390 | key { [ XF86HomePage ] };\ 1391 | key { [ XF86Reload ] };\ 1392 | key { [ XF86Close ] };\ 1393 | key { [ XF86ScrollUp ] };\ 1394 | key { [ XF86ScrollDown ] };\ 1395 | key { [ parenleft ] };\ 1396 | key { [ parenright ] };\ 1397 | key { [ XF86New ] };\ 1398 | key { [ Redo ] };\ 1399 | key { [ XF86Tools ] };\ 1400 | key { [ XF86Launch5 ] };\ 1401 | key { [ XF86Launch6 ] };\ 1402 | key { [ XF86Launch7 ] };\ 1403 | key { [ XF86Launch8 ] };\ 1404 | key { [ XF86Launch9 ] };\ 1405 | key { [ XF86AudioMicMute ] };\ 1406 | key { [ XF86TouchpadToggle ] };\ 1407 | key { [ XF86TouchpadOn ] };\ 1408 | key { [ XF86TouchpadOff ] };\ 1409 | key { [ Mode_switch ] };\ 1410 | key { [ NoSymbol, Alt_L ] };\ 1411 | key { [ NoSymbol, Meta_L ] };\ 1412 | key { [ NoSymbol, Super_L ] };\ 1413 | key { [ NoSymbol, Hyper_L ] };\ 1414 | key { [ XF86AudioPlay ] };\ 1415 | key { [ XF86AudioPause ] };\ 1416 | key { [ XF86Launch3 ] };\ 1417 | key { [ XF86Launch4 ] };\ 1418 | key { [ XF86LaunchB ] };\ 1419 | key { [ XF86Suspend ] };\ 1420 | key { [ XF86Close ] };\ 1421 | key { [ XF86AudioPlay ] };\ 1422 | key { [ XF86AudioForward ] };\ 1423 | key { [ Print ] };\ 1424 | key { [ XF86WebCam ] };\ 1425 | key { [ XF86AudioPreset ] };\ 1426 | key { [ XF86Mail ] };\ 1427 | key { [ XF86Messenger ] };\ 1428 | key { [ XF86Search ] };\ 1429 | key { [ XF86Go ] };\ 1430 | key { [ XF86Finance ] };\ 1431 | key { [ XF86Game ] };\ 1432 | key { [ XF86Shop ] };\ 1433 | key { [ Cancel ] };\ 1434 | key { [ XF86MonBrightnessDown ] };\ 1435 | key { [ XF86MonBrightnessUp ] };\ 1436 | key { [ XF86AudioMedia ] };\ 1437 | key { [ XF86Display ] };\ 1438 | key { [ XF86KbdLightOnOff ] };\ 1439 | key { [ XF86KbdBrightnessDown ] };\ 1440 | key { [ XF86KbdBrightnessUp ] };\ 1441 | key { [ XF86Send ] };\ 1442 | key { [ XF86Reply ] };\ 1443 | key { [ XF86MailForward ] };\ 1444 | key { [ XF86Save ] };\ 1445 | key { [ XF86Documents ] };\ 1446 | key { [ XF86Battery ] };\ 1447 | key { [ XF86Bluetooth ] };\ 1448 | key { [ XF86WLAN ] };\ 1449 | key { [ XF86UWB ] };\ 1450 | key { [ XF86WWAN ] };\ 1451 | key { [ XF86RFKill ] };\ 1452 | modifier_map Shift { , };\ 1453 | modifier_map Lock { };\ 1454 | modifier_map Control { , };\ 1455 | modifier_map Mod1 { , , };\ 1456 | modifier_map Mod2 { };\ 1457 | modifier_map Mod4 { , , , };\ 1458 | modifier_map Mod5 { , };\ 1459 | };\ 1460 | \ 1461 | };\ 1462 | "; 1463 | -------------------------------------------------------------------------------- /src/layouts.tpp: -------------------------------------------------------------------------------- 1 | std::vector> default_keys = { 2 | { 3 | {KEY_Q, "q", 1}, 4 | {KEY_W, "w", 1}, 5 | {KEY_E, "e", 1}, 6 | {KEY_R, "r", 1}, 7 | {KEY_T, "t", 1}, 8 | {KEY_Y, "y", 1}, 9 | {KEY_U, "u", 1}, 10 | {KEY_I, "i", 1}, 11 | {KEY_O, "o", 1}, 12 | {KEY_P, "p", 1}, 13 | {KEY_BACKSPACE, "⌫", 2} 14 | }, 15 | { 16 | {KEY_TAB, "⇥", 0.5}, 17 | {KEY_A, "a", 1}, 18 | {KEY_S, "s", 1}, 19 | {KEY_D, "d", 1}, 20 | {KEY_F, "f", 1}, 21 | {KEY_G, "g", 1}, 22 | {KEY_H, "h", 1}, 23 | {KEY_J, "j", 1}, 24 | {KEY_K, "k", 1}, 25 | {KEY_L, "l", 1}, 26 | {KEY_ENTER, "↵", 2} 27 | }, 28 | { 29 | {ABC_TOGGLE, "ABC", 1}, 30 | {KEY_Z, "z", 1}, 31 | {KEY_X, "x", 1}, 32 | {KEY_C, "c", 1}, 33 | {KEY_V, "v", 1}, 34 | {KEY_B, "b", 1}, 35 | {KEY_N, "n", 1}, 36 | {KEY_M, "m", 1}, 37 | {KEY_COMMA, ",", 1}, 38 | {KEY_DOT, ".", 1} 39 | }, 40 | { 41 | {NUM_TOGGLE, "123?", 1.5}, 42 | {KEY_SPACE, "_", 9.5}, 43 | {KEY_LEFT, "←", 0.5}, 44 | {KEY_RIGHT, "→", 0.5}, 45 | {KEY_UP, "↑", 0.5}, 46 | {KEY_DOWN, "↓", 0.5} 47 | } 48 | }; 49 | 50 | auto shift_keys = default_keys; 51 | for (auto& row : shift_keys) 52 | { 53 | for (auto& key : row) 54 | { 55 | key.text = key.text.uppercase(); 56 | if (key.code < USE_SHIFT) 57 | key.code |= USE_SHIFT; 58 | 59 | if (key.text == "ABC") 60 | key.text = "abc"; 61 | } 62 | } 63 | 64 | std::vector> numeric_keys = { 65 | { 66 | {KEY_1, "1", 1}, 67 | {KEY_2, "2", 1}, 68 | {KEY_3, "3", 1}, 69 | {KEY_4, "4", 1}, 70 | {KEY_5, "5", 1}, 71 | {KEY_6, "6", 1}, 72 | {KEY_7, "7", 1}, 73 | {KEY_8, "8", 1}, 74 | {KEY_9, "9", 1}, 75 | {KEY_0, "0", 1}, 76 | {KEY_MINUS, "-", 1}, 77 | {KEY_EQUAL, "=", 1}, 78 | {KEY_BACKSPACE, "⌫", 2} 79 | }, 80 | { 81 | {KEY_1 | USE_SHIFT, "!", 1}, 82 | {KEY_2 | USE_SHIFT, "@", 1}, 83 | {KEY_3 | USE_SHIFT, "#", 1}, 84 | {KEY_4 | USE_SHIFT, "$", 1}, 85 | {KEY_5 | USE_SHIFT, "%", 1}, 86 | {KEY_6 | USE_SHIFT, "^", 1}, 87 | {KEY_7 | USE_SHIFT, "&", 1}, 88 | {KEY_8 | USE_SHIFT, "*", 1}, 89 | {KEY_9 | USE_SHIFT, "(", 1}, 90 | {KEY_0 | USE_SHIFT, ")", 1}, 91 | {KEY_SEMICOLON, ";", 1}, 92 | {KEY_SEMICOLON | USE_SHIFT, ":", 1}, 93 | {KEY_ENTER, "↵", 3} 94 | }, 95 | { 96 | {KEY_LEFTBRACE, "[", 1}, 97 | {KEY_RIGHTBRACE, "]", 1}, 98 | {KEY_LEFTBRACE | USE_SHIFT, "{", 1}, 99 | {KEY_RIGHTBRACE | USE_SHIFT, "}", 1}, 100 | {KEY_COMMA | USE_SHIFT, "<", 1}, 101 | {KEY_DOT | USE_SHIFT, ">", 1}, 102 | {KEY_EQUAL | USE_SHIFT, "+", 1}, 103 | {KEY_SLASH, "/", 1}, 104 | {KEY_SLASH | USE_SHIFT, "?", 1}, 105 | {KEY_APOSTROPHE, "\'", 1}, 106 | {KEY_APOSTROPHE | USE_SHIFT, "\"", 1}, 107 | {KEY_GRAVE, "`", 1}, 108 | {KEY_GRAVE | USE_SHIFT, "~", 1}, 109 | {KEY_COMMA, ",", 1}, 110 | {KEY_DOT, ".", 1} 111 | }, 112 | { 113 | {ABC_TOGGLE, "abc", 1}, 114 | {KEY_SPACE, "_", 10}, 115 | {KEY_BACKSLASH, "\\", 1}, 116 | {KEY_BACKSLASH | USE_SHIFT, "|", 1} 117 | } 118 | }; 119 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "osk.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | #include "util/clara.hpp" 7 | 8 | #define ABC_TOGGLE 0x12345678 9 | #define NUM_TOGGLE 0x87654321 10 | 11 | #define IS_COMMAND(x) ((x) == ABC_TOGGLE || (x) == NUM_TOGGLE) 12 | 13 | #define USE_SHIFT 0x10000000 14 | 15 | namespace wf 16 | { 17 | namespace osk 18 | { 19 | int spacing = OSK_SPACING; 20 | int default_width = 800; 21 | int default_height = 400; 22 | int headerbar_size = 60; 23 | 24 | std::string anchor; 25 | 26 | KeyButton::KeyButton(Key key, int width, int height) 27 | { 28 | this->code = key.code; 29 | 30 | this->button.set_size_request(width, height); 31 | this->button.set_label(key.text); 32 | 33 | this->button.signal_pressed().connect_notify( 34 | sigc::mem_fun(this, &KeyButton::on_pressed)); 35 | this->button.signal_released().connect_notify( 36 | sigc::mem_fun(this, &KeyButton::on_released)); 37 | } 38 | 39 | void KeyButton::on_pressed() 40 | { 41 | auto& keyboard = Keyboard::get(); 42 | if (IS_COMMAND(this->code)) 43 | return; 44 | 45 | if (this->code & USE_SHIFT) 46 | keyboard.get_device().set_shift(true); 47 | 48 | keyboard.get_device().send_key(this->code & ~(USE_SHIFT), 49 | WL_KEYBOARD_KEY_STATE_PRESSED); 50 | } 51 | 52 | void KeyButton::on_released() 53 | { 54 | auto& keyboard = Keyboard::get(); 55 | if (IS_COMMAND(this->code)) 56 | return keyboard.handle_action(this->code); 57 | 58 | if (this->code & USE_SHIFT) 59 | keyboard.get_device().set_shift(false); 60 | 61 | keyboard.get_device().send_key(this->code & ~(USE_SHIFT), 62 | WL_KEYBOARD_KEY_STATE_RELEASED); 63 | } 64 | 65 | KeyboardRow::KeyboardRow(std::vector keys, 66 | int width, int height) 67 | { 68 | double sum = 0; 69 | for (auto& key : keys) 70 | sum += key.width; 71 | 72 | box.set_spacing(spacing); 73 | int total_spacing = std::min((int)keys.size() - 1, 0) * spacing; 74 | int total_buttons = width - total_spacing; 75 | 76 | for (auto& key : keys) 77 | { 78 | this->keys.emplace_back(std::make_unique 79 | (key, int(key.width / sum * total_buttons), height)); 80 | this->box.pack_start(this->keys.back()->button); 81 | } 82 | } 83 | 84 | KeyboardLayout::KeyboardLayout(std::vector> keys, 85 | int32_t width, int32_t height) 86 | { 87 | box.set_spacing(spacing); 88 | int total_spacing = std::min((int)keys.size() - 1, 0) * spacing; 89 | 90 | int row_height = (height - total_spacing) / keys.size(); 91 | for (auto& row : keys) 92 | { 93 | this->rows.emplace_back( 94 | std::make_unique (row, width, row_height)); 95 | this->box.pack_start(this->rows.back()->box); 96 | } 97 | } 98 | 99 | void Keyboard::init_layouts() 100 | { 101 | /* Key layouts are defined in layouts.tpp, 102 | * it defines default_keys, shift_keys, numeric_keys */ 103 | #include "layouts.tpp" 104 | 105 | this->default_layout = std::make_unique 106 | (default_keys, default_width, default_height); 107 | 108 | this->shift_layout = std::make_unique 109 | (shift_keys, default_width, default_height); 110 | 111 | this->numeric_layout = std::make_unique 112 | (numeric_keys, default_width, default_height); 113 | } 114 | 115 | void Keyboard::set_layout(KeyboardLayout *new_layout) 116 | { 117 | this->current_layout = new_layout; 118 | window->set_widget(new_layout->box); 119 | } 120 | 121 | Keyboard::Keyboard() 122 | { 123 | window = std::make_unique 124 | (default_width, default_height, anchor, headerbar_size); 125 | vk = std::make_unique (); 126 | 127 | init_layouts(); 128 | set_layout(default_layout.get()); 129 | } 130 | 131 | std::unique_ptr Keyboard::instance; 132 | void Keyboard::create() 133 | { 134 | if (instance) 135 | throw std::logic_error("Creating keyboard twice!"); 136 | 137 | instance = std::unique_ptr(new Keyboard()); 138 | } 139 | 140 | Keyboard& Keyboard::get() 141 | { 142 | if (!instance) 143 | throw std::logic_error("Getting keyboard before creating it!"); 144 | 145 | return *instance; 146 | } 147 | 148 | VirtualKeyboardDevice& Keyboard::get_device() 149 | { 150 | return *vk; 151 | } 152 | 153 | Gtk::Window& Keyboard::get_window() 154 | { 155 | return *window; 156 | } 157 | 158 | void Keyboard::handle_action(uint32_t action) 159 | { 160 | if (action == ABC_TOGGLE) 161 | { 162 | if (current_layout == default_layout.get()) { 163 | set_layout(shift_layout.get()); 164 | } else { 165 | set_layout(default_layout.get()); 166 | } 167 | } 168 | 169 | if (action == NUM_TOGGLE) 170 | set_layout(numeric_layout.get()); 171 | } 172 | } 173 | } 174 | 175 | int main(int argc, char **argv) 176 | { 177 | bool show_help = false; 178 | 179 | auto cli = clara::detail::Help(show_help) | 180 | clara::detail::Opt(wf::osk::default_width, "int")["-w"]["--width"] 181 | ("keyboard width") | 182 | clara::detail::Opt(wf::osk::default_height, "int")["-h"]["--height"] 183 | ("keyboard height") | 184 | clara::detail::Opt(wf::osk::headerbar_size, "int")["-b"]["--headerbar-height"] 185 | ("headerbar height") | 186 | clara::detail::Opt(wf::osk::anchor, "top|left|bottom|right|pinned")["-a"] 187 | ["--anchor"]("where the keyboard should anchor in the screen"); 188 | 189 | auto res = cli.parse(clara::detail::Args(argc, argv)); 190 | if (!res) { 191 | std::cerr << "Error: " << res.errorMessage() << std::endl; 192 | return 1; 193 | } 194 | 195 | if (show_help) { 196 | std::cout << cli << std::endl; 197 | return 0; 198 | } 199 | 200 | auto app = Gtk::Application::create(); 201 | wf::osk::Keyboard::create(); 202 | return app->run(wf::osk::Keyboard::get().get_window()); 203 | } 204 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | executable('wf-osk', ['main.cpp', 'wayland-window.cpp', 'virtual-keyboard.cpp', 'shared/os-compatibility.c'], 2 | dependencies: [gtkmm, wf_protos, gtkls], 3 | install: true) 4 | -------------------------------------------------------------------------------- /src/osk.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "virtual-keyboard.hpp" 10 | #include "wayland-window.hpp" 11 | 12 | namespace wf 13 | { 14 | namespace osk 15 | { 16 | extern int spacing; 17 | 18 | struct Key 19 | { 20 | uint32_t code; 21 | Glib::ustring text; 22 | double width; 23 | }; 24 | 25 | struct KeyButton 26 | { 27 | Gtk::Button button; 28 | 29 | /* keycode as in linux/input-event-codes.h */ 30 | uint32_t code; 31 | KeyButton(Key key, int width, int height); 32 | 33 | private: 34 | void on_pressed(); 35 | void on_released(); 36 | }; 37 | 38 | struct KeyboardRow 39 | { 40 | Gtk::HBox box; 41 | std::vector> keys; 42 | 43 | KeyboardRow(std::vector keys, 44 | int width, int height); 45 | }; 46 | 47 | struct KeyboardLayout 48 | { 49 | Gtk::VBox box; 50 | std::vector> rows; 51 | 52 | KeyboardLayout(std::vector> keys, 53 | int32_t width, int32_t height); 54 | }; 55 | 56 | class Keyboard 57 | { 58 | std::unique_ptr default_layout, shift_layout, 59 | numeric_layout; 60 | KeyboardLayout *current_layout = nullptr; 61 | void init_layouts(); 62 | void set_layout(KeyboardLayout *new_layout); 63 | 64 | std::unique_ptr window; 65 | std::unique_ptr vk; 66 | Keyboard(); 67 | 68 | static std::unique_ptr instance; 69 | 70 | public: 71 | static void create(); 72 | static Keyboard& get(); 73 | 74 | void handle_action(uint32_t action); 75 | VirtualKeyboardDevice& get_device(); 76 | Gtk::Window& get_window(); 77 | }; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/shared/os-compatibility.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2012 Collabora, Ltd. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice (including the 13 | * next paragraph) shall be included in all copies or substantial 14 | * portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #define _POSIX_C_SOURCE 200809L 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "os-compatibility.h" 38 | 39 | int 40 | os_fd_set_cloexec(int fd) 41 | { 42 | long flags; 43 | 44 | if (fd == -1) 45 | return -1; 46 | 47 | flags = fcntl(fd, F_GETFD); 48 | if (flags == -1) 49 | return -1; 50 | 51 | if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) 52 | return -1; 53 | 54 | return 0; 55 | } 56 | 57 | static int 58 | set_cloexec_or_close(int fd) 59 | { 60 | if (os_fd_set_cloexec(fd) != 0) { 61 | close(fd); 62 | return -1; 63 | } 64 | return fd; 65 | } 66 | 67 | int 68 | os_socketpair_cloexec(int domain, int type, int protocol, int *sv) 69 | { 70 | int ret; 71 | 72 | #ifdef SOCK_CLOEXEC 73 | ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv); 74 | if (ret == 0 || errno != EINVAL) 75 | return ret; 76 | #endif 77 | 78 | ret = socketpair(domain, type, protocol, sv); 79 | if (ret < 0) 80 | return ret; 81 | 82 | sv[0] = set_cloexec_or_close(sv[0]); 83 | sv[1] = set_cloexec_or_close(sv[1]); 84 | 85 | if (sv[0] != -1 && sv[1] != -1) 86 | return 0; 87 | 88 | close(sv[0]); 89 | close(sv[1]); 90 | return -1; 91 | } 92 | 93 | int 94 | os_epoll_create_cloexec(void) 95 | { 96 | int fd; 97 | 98 | #ifdef EPOLL_CLOEXEC 99 | fd = epoll_create1(EPOLL_CLOEXEC); 100 | if (fd >= 0) 101 | return fd; 102 | if (errno != EINVAL) 103 | return -1; 104 | #endif 105 | 106 | fd = epoll_create(1); 107 | return set_cloexec_or_close(fd); 108 | } 109 | 110 | static int 111 | create_tmpfile_cloexec(char *tmpname) 112 | { 113 | int fd; 114 | 115 | #ifdef HAVE_MKOSTEMP 116 | fd = mkostemp(tmpname, O_CLOEXEC); 117 | if (fd >= 0) 118 | unlink(tmpname); 119 | #else 120 | fd = mkstemp(tmpname); 121 | if (fd >= 0) { 122 | fd = set_cloexec_or_close(fd); 123 | unlink(tmpname); 124 | } 125 | #endif 126 | 127 | return fd; 128 | } 129 | 130 | /* 131 | * Create a new, unique, anonymous file of the given size, and 132 | * return the file descriptor for it. The file descriptor is set 133 | * CLOEXEC. The file is immediately suitable for mmap()'ing 134 | * the given size at offset zero. 135 | * 136 | * The file should not have a permanent backing store like a disk, 137 | * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. 138 | * 139 | * The file name is deleted from the file system. 140 | * 141 | * The file is suitable for buffer sharing between processes by 142 | * transmitting the file descriptor over Unix sockets using the 143 | * SCM_RIGHTS methods. 144 | * 145 | * If the C library implements posix_fallocate(), it is used to 146 | * guarantee that disk space is available for the file at the 147 | * given size. If disk space is insufficient, errno is set to ENOSPC. 148 | * If posix_fallocate() is not supported, program may receive 149 | * SIGBUS on accessing mmap()'ed file contents instead. 150 | */ 151 | int 152 | os_create_anonymous_file(off_t size) 153 | { 154 | static const char template[] = "/weston-shared-XXXXXX"; 155 | const char *path; 156 | char *name; 157 | int fd; 158 | int ret; 159 | 160 | path = getenv("XDG_RUNTIME_DIR"); 161 | if (!path) { 162 | errno = ENOENT; 163 | return -1; 164 | } 165 | 166 | name = malloc(strlen(path) + sizeof(template)); 167 | if (!name) 168 | return -1; 169 | 170 | strcpy(name, path); 171 | strcat(name, template); 172 | 173 | fd = create_tmpfile_cloexec(name); 174 | 175 | free(name); 176 | 177 | if (fd < 0) 178 | return -1; 179 | 180 | #ifdef HAVE_POSIX_FALLOCATE 181 | do { 182 | ret = posix_fallocate(fd, 0, size); 183 | } while (ret == EINTR); 184 | if (ret != 0) { 185 | close(fd); 186 | errno = ret; 187 | return -1; 188 | } 189 | #else 190 | do { 191 | ret = ftruncate(fd, size); 192 | } while (ret < 0 && errno == EINTR); 193 | if (ret < 0) { 194 | close(fd); 195 | return -1; 196 | } 197 | #endif 198 | 199 | return fd; 200 | } 201 | 202 | #ifndef MISSING_STRCHRNUL 203 | char * 204 | strchrnul(const char *s, int c) 205 | { 206 | while (*s && *s != c) 207 | s++; 208 | return (char *)s; 209 | } 210 | #endif 211 | -------------------------------------------------------------------------------- /src/shared/os-compatibility.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2012 Collabora, Ltd. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice (including the 13 | * next paragraph) shall be included in all copies or substantial 14 | * portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 20 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 21 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | #ifndef OS_COMPATIBILITY_H 27 | #define OS_COMPATIBILITY_H 28 | 29 | #include 30 | #ifdef __cplusplus 31 | extern "C" 32 | { 33 | int os_fd_set_cloexec(int fd); 34 | int os_socketpair_cloexec(int domain, int type, int protocol, int *sv); 35 | int os_epoll_create_cloexec(void); 36 | int os_create_anonymous_file(off_t size); 37 | #ifdef MISSING_STRCHRNUL 38 | char * strchrnul(const char *s, int c); 39 | #endif 40 | } 41 | #endif 42 | 43 | #endif /* OS_COMPATIBILITY_H */ 44 | -------------------------------------------------------------------------------- /src/util/clara.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Two Blue Cubes Ltd. All rights reserved. 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 | // 6 | // See https://github.com/philsquared/Clara for more details 7 | 8 | // Clara v1.1.5 9 | 10 | #ifndef CLARA_HPP_INCLUDED 11 | #define CLARA_HPP_INCLUDED 12 | 13 | #ifndef CLARA_CONFIG_CONSOLE_WIDTH 14 | #define CLARA_CONFIG_CONSOLE_WIDTH 80 15 | #endif 16 | 17 | #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 18 | #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH 19 | #endif 20 | 21 | #ifndef CLARA_CONFIG_OPTIONAL_TYPE 22 | #ifdef __has_include 23 | #if __has_include() && __cplusplus >= 201703L 24 | #include 25 | #define CLARA_CONFIG_OPTIONAL_TYPE std::optional 26 | #endif 27 | #endif 28 | #endif 29 | 30 | 31 | // ----------- #included from clara_textflow.hpp ----------- 32 | 33 | // TextFlowCpp 34 | // 35 | // A single-header library for wrapping and laying out basic text, by Phil Nash 36 | // 37 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 38 | // file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 39 | // 40 | // This project is hosted at https://github.com/philsquared/textflowcpp 41 | 42 | #ifndef CLARA_TEXTFLOW_HPP_INCLUDED 43 | #define CLARA_TEXTFLOW_HPP_INCLUDED 44 | 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 51 | #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 52 | #endif 53 | 54 | 55 | namespace clara { namespace TextFlow { 56 | 57 | inline auto isWhitespace( char c ) -> bool { 58 | static std::string chars = " \t\n\r"; 59 | return chars.find( c ) != std::string::npos; 60 | } 61 | inline auto isBreakableBefore( char c ) -> bool { 62 | static std::string chars = "[({<|"; 63 | return chars.find( c ) != std::string::npos; 64 | } 65 | inline auto isBreakableAfter( char c ) -> bool { 66 | static std::string chars = "])}>.,:;*+-=&/\\"; 67 | return chars.find( c ) != std::string::npos; 68 | } 69 | 70 | class Columns; 71 | 72 | class Column { 73 | std::vector m_strings; 74 | size_t m_width = CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; 75 | size_t m_indent = 0; 76 | size_t m_initialIndent = std::string::npos; 77 | 78 | public: 79 | class iterator { 80 | friend Column; 81 | 82 | Column const& m_column; 83 | size_t m_stringIndex = 0; 84 | size_t m_pos = 0; 85 | 86 | size_t m_len = 0; 87 | size_t m_end = 0; 88 | bool m_suffix = false; 89 | 90 | iterator( Column const& column, size_t stringIndex ) 91 | : m_column( column ), 92 | m_stringIndex( stringIndex ) 93 | {} 94 | 95 | auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } 96 | 97 | auto isBoundary( size_t at ) const -> bool { 98 | assert( at > 0 ); 99 | assert( at <= line().size() ); 100 | 101 | return at == line().size() || 102 | ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || 103 | isBreakableBefore( line()[at] ) || 104 | isBreakableAfter( line()[at-1] ); 105 | } 106 | 107 | void calcLength() { 108 | assert( m_stringIndex < m_column.m_strings.size() ); 109 | 110 | m_suffix = false; 111 | auto width = m_column.m_width-indent(); 112 | m_end = m_pos; 113 | while( m_end < line().size() && line()[m_end] != '\n' ) 114 | ++m_end; 115 | 116 | if( m_end < m_pos + width ) { 117 | m_len = m_end - m_pos; 118 | } 119 | else { 120 | size_t len = width; 121 | while (len > 0 && !isBoundary(m_pos + len)) 122 | --len; 123 | while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) 124 | --len; 125 | 126 | if (len > 0) { 127 | m_len = len; 128 | } else { 129 | m_suffix = true; 130 | m_len = width - 1; 131 | } 132 | } 133 | } 134 | 135 | auto indent() const -> size_t { 136 | auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; 137 | return initial == std::string::npos ? m_column.m_indent : initial; 138 | } 139 | 140 | auto addIndentAndSuffix(std::string const &plain) const -> std::string { 141 | return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); 142 | } 143 | 144 | public: 145 | using difference_type = std::ptrdiff_t; 146 | using value_type = std::string; 147 | using pointer = value_type*; 148 | using reference = value_type&; 149 | using iterator_category = std::forward_iterator_tag; 150 | 151 | explicit iterator( Column const& column ) : m_column( column ) { 152 | assert( m_column.m_width > m_column.m_indent ); 153 | assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); 154 | calcLength(); 155 | if( m_len == 0 ) 156 | m_stringIndex++; // Empty string 157 | } 158 | 159 | auto operator *() const -> std::string { 160 | assert( m_stringIndex < m_column.m_strings.size() ); 161 | assert( m_pos <= m_end ); 162 | return addIndentAndSuffix(line().substr(m_pos, m_len)); 163 | } 164 | 165 | auto operator ++() -> iterator& { 166 | m_pos += m_len; 167 | if( m_pos < line().size() && line()[m_pos] == '\n' ) 168 | m_pos += 1; 169 | else 170 | while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) 171 | ++m_pos; 172 | 173 | if( m_pos == line().size() ) { 174 | m_pos = 0; 175 | ++m_stringIndex; 176 | } 177 | if( m_stringIndex < m_column.m_strings.size() ) 178 | calcLength(); 179 | return *this; 180 | } 181 | auto operator ++(int) -> iterator { 182 | iterator prev( *this ); 183 | operator++(); 184 | return prev; 185 | } 186 | 187 | auto operator ==( iterator const& other ) const -> bool { 188 | return 189 | m_pos == other.m_pos && 190 | m_stringIndex == other.m_stringIndex && 191 | &m_column == &other.m_column; 192 | } 193 | auto operator !=( iterator const& other ) const -> bool { 194 | return !operator==( other ); 195 | } 196 | }; 197 | using const_iterator = iterator; 198 | 199 | explicit Column( std::string const& text ) { m_strings.push_back( text ); } 200 | 201 | auto width( size_t newWidth ) -> Column& { 202 | assert( newWidth > 0 ); 203 | m_width = newWidth; 204 | return *this; 205 | } 206 | auto indent( size_t newIndent ) -> Column& { 207 | m_indent = newIndent; 208 | return *this; 209 | } 210 | auto initialIndent( size_t newIndent ) -> Column& { 211 | m_initialIndent = newIndent; 212 | return *this; 213 | } 214 | 215 | auto width() const -> size_t { return m_width; } 216 | auto begin() const -> iterator { return iterator( *this ); } 217 | auto end() const -> iterator { return { *this, m_strings.size() }; } 218 | 219 | inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { 220 | bool first = true; 221 | for( auto line : col ) { 222 | if( first ) 223 | first = false; 224 | else 225 | os << "\n"; 226 | os << line; 227 | } 228 | return os; 229 | } 230 | 231 | auto operator + ( Column const& other ) -> Columns; 232 | 233 | auto toString() const -> std::string { 234 | std::ostringstream oss; 235 | oss << *this; 236 | return oss.str(); 237 | } 238 | }; 239 | 240 | class Spacer : public Column { 241 | 242 | public: 243 | explicit Spacer( size_t spaceWidth ) : Column( "" ) { 244 | width( spaceWidth ); 245 | } 246 | }; 247 | 248 | class Columns { 249 | std::vector m_columns; 250 | 251 | public: 252 | 253 | class iterator { 254 | friend Columns; 255 | struct EndTag {}; 256 | 257 | std::vector const& m_columns; 258 | std::vector m_iterators; 259 | size_t m_activeIterators; 260 | 261 | iterator( Columns const& columns, EndTag ) 262 | : m_columns( columns.m_columns ), 263 | m_activeIterators( 0 ) 264 | { 265 | m_iterators.reserve( m_columns.size() ); 266 | 267 | for( auto const& col : m_columns ) 268 | m_iterators.push_back( col.end() ); 269 | } 270 | 271 | public: 272 | using difference_type = std::ptrdiff_t; 273 | using value_type = std::string; 274 | using pointer = value_type*; 275 | using reference = value_type&; 276 | using iterator_category = std::forward_iterator_tag; 277 | 278 | explicit iterator( Columns const& columns ) 279 | : m_columns( columns.m_columns ), 280 | m_activeIterators( m_columns.size() ) 281 | { 282 | m_iterators.reserve( m_columns.size() ); 283 | 284 | for( auto const& col : m_columns ) 285 | m_iterators.push_back( col.begin() ); 286 | } 287 | 288 | auto operator ==( iterator const& other ) const -> bool { 289 | return m_iterators == other.m_iterators; 290 | } 291 | auto operator !=( iterator const& other ) const -> bool { 292 | return m_iterators != other.m_iterators; 293 | } 294 | auto operator *() const -> std::string { 295 | std::string row, padding; 296 | 297 | for( size_t i = 0; i < m_columns.size(); ++i ) { 298 | auto width = m_columns[i].width(); 299 | if( m_iterators[i] != m_columns[i].end() ) { 300 | std::string col = *m_iterators[i]; 301 | row += padding + col; 302 | if( col.size() < width ) 303 | padding = std::string( width - col.size(), ' ' ); 304 | else 305 | padding = ""; 306 | } 307 | else { 308 | padding += std::string( width, ' ' ); 309 | } 310 | } 311 | return row; 312 | } 313 | auto operator ++() -> iterator& { 314 | for( size_t i = 0; i < m_columns.size(); ++i ) { 315 | if (m_iterators[i] != m_columns[i].end()) 316 | ++m_iterators[i]; 317 | } 318 | return *this; 319 | } 320 | auto operator ++(int) -> iterator { 321 | iterator prev( *this ); 322 | operator++(); 323 | return prev; 324 | } 325 | }; 326 | using const_iterator = iterator; 327 | 328 | auto begin() const -> iterator { return iterator( *this ); } 329 | auto end() const -> iterator { return { *this, iterator::EndTag() }; } 330 | 331 | auto operator += ( Column const& col ) -> Columns& { 332 | m_columns.push_back( col ); 333 | return *this; 334 | } 335 | auto operator + ( Column const& col ) -> Columns { 336 | Columns combined = *this; 337 | combined += col; 338 | return combined; 339 | } 340 | 341 | inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { 342 | 343 | bool first = true; 344 | for( auto line : cols ) { 345 | if( first ) 346 | first = false; 347 | else 348 | os << "\n"; 349 | os << line; 350 | } 351 | return os; 352 | } 353 | 354 | auto toString() const -> std::string { 355 | std::ostringstream oss; 356 | oss << *this; 357 | return oss.str(); 358 | } 359 | }; 360 | 361 | inline auto Column::operator + ( Column const& other ) -> Columns { 362 | Columns cols; 363 | cols += *this; 364 | cols += other; 365 | return cols; 366 | } 367 | }} 368 | 369 | #endif // CLARA_TEXTFLOW_HPP_INCLUDED 370 | 371 | // ----------- end of #include from clara_textflow.hpp ----------- 372 | // ........... back in clara.hpp 373 | 374 | 375 | #include 376 | #include 377 | #include 378 | 379 | #if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) 380 | #define CLARA_PLATFORM_WINDOWS 381 | #endif 382 | 383 | namespace clara { 384 | namespace detail { 385 | 386 | // Traits for extracting arg and return type of lambdas (for single argument lambdas) 387 | template 388 | struct UnaryLambdaTraits : UnaryLambdaTraits {}; 389 | 390 | template 391 | struct UnaryLambdaTraits { 392 | static const bool isValid = false; 393 | }; 394 | 395 | template 396 | struct UnaryLambdaTraits { 397 | static const bool isValid = true; 398 | using ArgType = typename std::remove_const::type>::type; 399 | using ReturnType = ReturnT; 400 | }; 401 | 402 | class TokenStream; 403 | 404 | // Transport for raw args (copied from main args, or supplied via init list for testing) 405 | class Args { 406 | friend TokenStream; 407 | std::string m_exeName; 408 | std::vector m_args; 409 | 410 | public: 411 | Args( int argc, char const* const* argv ) 412 | : m_exeName(argv[0]), 413 | m_args(argv + 1, argv + argc) {} 414 | 415 | Args( std::initializer_list args ) 416 | : m_exeName( *args.begin() ), 417 | m_args( args.begin()+1, args.end() ) 418 | {} 419 | 420 | auto exeName() const -> std::string { 421 | return m_exeName; 422 | } 423 | }; 424 | 425 | // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string 426 | // may encode an option + its argument if the : or = form is used 427 | enum class TokenType { 428 | Option, Argument 429 | }; 430 | struct Token { 431 | TokenType type; 432 | std::string token; 433 | }; 434 | 435 | inline auto isOptPrefix( char c ) -> bool { 436 | return c == '-' 437 | #ifdef CLARA_PLATFORM_WINDOWS 438 | || c == '/' 439 | #endif 440 | ; 441 | } 442 | 443 | // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled 444 | class TokenStream { 445 | using Iterator = std::vector::const_iterator; 446 | Iterator it; 447 | Iterator itEnd; 448 | std::vector m_tokenBuffer; 449 | 450 | void loadBuffer() { 451 | m_tokenBuffer.resize( 0 ); 452 | 453 | // Skip any empty strings 454 | while( it != itEnd && it->empty() ) 455 | ++it; 456 | 457 | if( it != itEnd ) { 458 | auto const &next = *it; 459 | if( isOptPrefix( next[0] ) ) { 460 | auto delimiterPos = next.find_first_of( " :=" ); 461 | if( delimiterPos != std::string::npos ) { 462 | m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); 463 | m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); 464 | } else { 465 | if( next[1] != '-' && next.size() > 2 ) { 466 | std::string opt = "- "; 467 | for( size_t i = 1; i < next.size(); ++i ) { 468 | opt[1] = next[i]; 469 | m_tokenBuffer.push_back( { TokenType::Option, opt } ); 470 | } 471 | } else { 472 | m_tokenBuffer.push_back( { TokenType::Option, next } ); 473 | } 474 | } 475 | } else { 476 | m_tokenBuffer.push_back( { TokenType::Argument, next } ); 477 | } 478 | } 479 | } 480 | 481 | public: 482 | explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} 483 | 484 | TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { 485 | loadBuffer(); 486 | } 487 | 488 | explicit operator bool() const { 489 | return !m_tokenBuffer.empty() || it != itEnd; 490 | } 491 | 492 | auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } 493 | 494 | auto operator*() const -> Token { 495 | assert( !m_tokenBuffer.empty() ); 496 | return m_tokenBuffer.front(); 497 | } 498 | 499 | auto operator->() const -> Token const * { 500 | assert( !m_tokenBuffer.empty() ); 501 | return &m_tokenBuffer.front(); 502 | } 503 | 504 | auto operator++() -> TokenStream & { 505 | if( m_tokenBuffer.size() >= 2 ) { 506 | m_tokenBuffer.erase( m_tokenBuffer.begin() ); 507 | } else { 508 | if( it != itEnd ) 509 | ++it; 510 | loadBuffer(); 511 | } 512 | return *this; 513 | } 514 | }; 515 | 516 | 517 | class ResultBase { 518 | public: 519 | enum Type { 520 | Ok, LogicError, RuntimeError 521 | }; 522 | 523 | protected: 524 | ResultBase( Type type ) : m_type( type ) {} 525 | virtual ~ResultBase() = default; 526 | 527 | virtual void enforceOk() const = 0; 528 | 529 | Type m_type; 530 | }; 531 | 532 | template 533 | class ResultValueBase : public ResultBase { 534 | public: 535 | auto value() const -> T const & { 536 | enforceOk(); 537 | return m_value; 538 | } 539 | 540 | protected: 541 | ResultValueBase( Type type ) : ResultBase( type ) {} 542 | 543 | ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { 544 | if( m_type == ResultBase::Ok ) 545 | new( &m_value ) T( other.m_value ); 546 | } 547 | 548 | ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { 549 | new( &m_value ) T( value ); 550 | } 551 | 552 | auto operator=( ResultValueBase const &other ) -> ResultValueBase & { 553 | if( m_type == ResultBase::Ok ) 554 | m_value.~T(); 555 | ResultBase::operator=(other); 556 | if( m_type == ResultBase::Ok ) 557 | new( &m_value ) T( other.m_value ); 558 | return *this; 559 | } 560 | 561 | ~ResultValueBase() override { 562 | if( m_type == Ok ) 563 | m_value.~T(); 564 | } 565 | 566 | union { 567 | T m_value; 568 | }; 569 | }; 570 | 571 | template<> 572 | class ResultValueBase : public ResultBase { 573 | protected: 574 | using ResultBase::ResultBase; 575 | }; 576 | 577 | template 578 | class BasicResult : public ResultValueBase { 579 | public: 580 | template 581 | explicit BasicResult( BasicResult const &other ) 582 | : ResultValueBase( other.type() ), 583 | m_errorMessage( other.errorMessage() ) 584 | { 585 | assert( type() != ResultBase::Ok ); 586 | } 587 | 588 | template 589 | static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } 590 | static auto ok() -> BasicResult { return { ResultBase::Ok }; } 591 | static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } 592 | static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } 593 | 594 | explicit operator bool() const { return m_type == ResultBase::Ok; } 595 | auto type() const -> ResultBase::Type { return m_type; } 596 | auto errorMessage() const -> std::string { return m_errorMessage; } 597 | 598 | protected: 599 | void enforceOk() const override { 600 | 601 | // Errors shouldn't reach this point, but if they do 602 | // the actual error message will be in m_errorMessage 603 | assert( m_type != ResultBase::LogicError ); 604 | assert( m_type != ResultBase::RuntimeError ); 605 | if( m_type != ResultBase::Ok ) 606 | std::abort(); 607 | } 608 | 609 | std::string m_errorMessage; // Only populated if resultType is an error 610 | 611 | BasicResult( ResultBase::Type type, std::string const &message ) 612 | : ResultValueBase(type), 613 | m_errorMessage(message) 614 | { 615 | assert( m_type != ResultBase::Ok ); 616 | } 617 | 618 | using ResultValueBase::ResultValueBase; 619 | using ResultBase::m_type; 620 | }; 621 | 622 | enum class ParseResultType { 623 | Matched, NoMatch, ShortCircuitAll, ShortCircuitSame 624 | }; 625 | 626 | class ParseState { 627 | public: 628 | 629 | ParseState( ParseResultType type, TokenStream const &remainingTokens ) 630 | : m_type(type), 631 | m_remainingTokens( remainingTokens ) 632 | {} 633 | 634 | auto type() const -> ParseResultType { return m_type; } 635 | auto remainingTokens() const -> TokenStream { return m_remainingTokens; } 636 | 637 | private: 638 | ParseResultType m_type; 639 | TokenStream m_remainingTokens; 640 | }; 641 | 642 | using Result = BasicResult; 643 | using ParserResult = BasicResult; 644 | using InternalParseResult = BasicResult; 645 | 646 | struct HelpColumns { 647 | std::string left; 648 | std::string right; 649 | }; 650 | 651 | template 652 | inline auto convertInto( std::string const &source, T& target ) -> ParserResult { 653 | std::stringstream ss; 654 | ss << source; 655 | ss >> target; 656 | if( ss.fail() ) 657 | return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); 658 | else 659 | return ParserResult::ok( ParseResultType::Matched ); 660 | } 661 | inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { 662 | target = source; 663 | return ParserResult::ok( ParseResultType::Matched ); 664 | } 665 | inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { 666 | std::string srcLC = source; 667 | std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); 668 | if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") 669 | target = true; 670 | else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") 671 | target = false; 672 | else 673 | return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); 674 | return ParserResult::ok( ParseResultType::Matched ); 675 | } 676 | #ifdef CLARA_CONFIG_OPTIONAL_TYPE 677 | template 678 | inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { 679 | T temp; 680 | auto result = convertInto( source, temp ); 681 | if( result ) 682 | target = std::move(temp); 683 | return result; 684 | } 685 | #endif // CLARA_CONFIG_OPTIONAL_TYPE 686 | 687 | struct NonCopyable { 688 | NonCopyable() = default; 689 | NonCopyable( NonCopyable const & ) = delete; 690 | NonCopyable( NonCopyable && ) = delete; 691 | NonCopyable &operator=( NonCopyable const & ) = delete; 692 | NonCopyable &operator=( NonCopyable && ) = delete; 693 | }; 694 | 695 | struct BoundRef : NonCopyable { 696 | virtual ~BoundRef() = default; 697 | virtual auto isContainer() const -> bool { return false; } 698 | virtual auto isFlag() const -> bool { return false; } 699 | }; 700 | struct BoundValueRefBase : BoundRef { 701 | virtual auto setValue( std::string const &arg ) -> ParserResult = 0; 702 | }; 703 | struct BoundFlagRefBase : BoundRef { 704 | virtual auto setFlag( bool flag ) -> ParserResult = 0; 705 | virtual auto isFlag() const -> bool { return true; } 706 | }; 707 | 708 | template 709 | struct BoundValueRef : BoundValueRefBase { 710 | T &m_ref; 711 | 712 | explicit BoundValueRef( T &ref ) : m_ref( ref ) {} 713 | 714 | auto setValue( std::string const &arg ) -> ParserResult override { 715 | return convertInto( arg, m_ref ); 716 | } 717 | }; 718 | 719 | template 720 | struct BoundValueRef> : BoundValueRefBase { 721 | std::vector &m_ref; 722 | 723 | explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} 724 | 725 | auto isContainer() const -> bool override { return true; } 726 | 727 | auto setValue( std::string const &arg ) -> ParserResult override { 728 | T temp; 729 | auto result = convertInto( arg, temp ); 730 | if( result ) 731 | m_ref.push_back( temp ); 732 | return result; 733 | } 734 | }; 735 | 736 | struct BoundFlagRef : BoundFlagRefBase { 737 | bool &m_ref; 738 | 739 | explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} 740 | 741 | auto setFlag( bool flag ) -> ParserResult override { 742 | m_ref = flag; 743 | return ParserResult::ok( ParseResultType::Matched ); 744 | } 745 | }; 746 | 747 | template 748 | struct LambdaInvoker { 749 | static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); 750 | 751 | template 752 | static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { 753 | return lambda( arg ); 754 | } 755 | }; 756 | 757 | template<> 758 | struct LambdaInvoker { 759 | template 760 | static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { 761 | lambda( arg ); 762 | return ParserResult::ok( ParseResultType::Matched ); 763 | } 764 | }; 765 | 766 | template 767 | inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { 768 | ArgType temp{}; 769 | auto result = convertInto( arg, temp ); 770 | return !result 771 | ? result 772 | : LambdaInvoker::ReturnType>::invoke( lambda, temp ); 773 | } 774 | 775 | 776 | template 777 | struct BoundLambda : BoundValueRefBase { 778 | L m_lambda; 779 | 780 | static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); 781 | explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} 782 | 783 | auto setValue( std::string const &arg ) -> ParserResult override { 784 | return invokeLambda::ArgType>( m_lambda, arg ); 785 | } 786 | }; 787 | 788 | template 789 | struct BoundFlagLambda : BoundFlagRefBase { 790 | L m_lambda; 791 | 792 | static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); 793 | static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); 794 | 795 | explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} 796 | 797 | auto setFlag( bool flag ) -> ParserResult override { 798 | return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); 799 | } 800 | }; 801 | 802 | enum class Optionality { Optional, Required }; 803 | 804 | struct Parser; 805 | 806 | class ParserBase { 807 | public: 808 | virtual ~ParserBase() = default; 809 | virtual auto validate() const -> Result { return Result::ok(); } 810 | virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; 811 | virtual auto cardinality() const -> size_t { return 1; } 812 | 813 | auto parse( Args const &args ) const -> InternalParseResult { 814 | return parse( args.exeName(), TokenStream( args ) ); 815 | } 816 | }; 817 | 818 | template 819 | class ComposableParserImpl : public ParserBase { 820 | public: 821 | template 822 | auto operator|( T const &other ) const -> Parser; 823 | 824 | template 825 | auto operator+( T const &other ) const -> Parser; 826 | }; 827 | 828 | // Common code and state for Args and Opts 829 | template 830 | class ParserRefImpl : public ComposableParserImpl { 831 | protected: 832 | Optionality m_optionality = Optionality::Optional; 833 | std::shared_ptr m_ref; 834 | std::string m_hint; 835 | std::string m_description; 836 | 837 | explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} 838 | 839 | public: 840 | template 841 | ParserRefImpl( T &ref, std::string const &hint ) 842 | : m_ref( std::make_shared>( ref ) ), 843 | m_hint( hint ) 844 | {} 845 | 846 | template 847 | ParserRefImpl( LambdaT const &ref, std::string const &hint ) 848 | : m_ref( std::make_shared>( ref ) ), 849 | m_hint(hint) 850 | {} 851 | 852 | auto operator()( std::string const &description ) -> DerivedT & { 853 | m_description = description; 854 | return static_cast( *this ); 855 | } 856 | 857 | auto optional() -> DerivedT & { 858 | m_optionality = Optionality::Optional; 859 | return static_cast( *this ); 860 | }; 861 | 862 | auto required() -> DerivedT & { 863 | m_optionality = Optionality::Required; 864 | return static_cast( *this ); 865 | }; 866 | 867 | auto isOptional() const -> bool { 868 | return m_optionality == Optionality::Optional; 869 | } 870 | 871 | auto cardinality() const -> size_t override { 872 | if( m_ref->isContainer() ) 873 | return 0; 874 | else 875 | return 1; 876 | } 877 | 878 | auto hint() const -> std::string { return m_hint; } 879 | }; 880 | 881 | class ExeName : public ComposableParserImpl { 882 | std::shared_ptr m_name; 883 | std::shared_ptr m_ref; 884 | 885 | template 886 | static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { 887 | return std::make_shared>( lambda) ; 888 | } 889 | 890 | public: 891 | ExeName() : m_name( std::make_shared( "" ) ) {} 892 | 893 | explicit ExeName( std::string &ref ) : ExeName() { 894 | m_ref = std::make_shared>( ref ); 895 | } 896 | 897 | template 898 | explicit ExeName( LambdaT const& lambda ) : ExeName() { 899 | m_ref = std::make_shared>( lambda ); 900 | } 901 | 902 | // The exe name is not parsed out of the normal tokens, but is handled specially 903 | auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { 904 | return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); 905 | } 906 | 907 | auto name() const -> std::string { return *m_name; } 908 | auto set( std::string const& newName ) -> ParserResult { 909 | 910 | auto lastSlash = newName.find_last_of( "\\/" ); 911 | auto filename = ( lastSlash == std::string::npos ) 912 | ? newName 913 | : newName.substr( lastSlash+1 ); 914 | 915 | *m_name = filename; 916 | if( m_ref ) 917 | return m_ref->setValue( filename ); 918 | else 919 | return ParserResult::ok( ParseResultType::Matched ); 920 | } 921 | }; 922 | 923 | class Arg : public ParserRefImpl { 924 | public: 925 | using ParserRefImpl::ParserRefImpl; 926 | 927 | auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { 928 | auto validationResult = validate(); 929 | if( !validationResult ) 930 | return InternalParseResult( validationResult ); 931 | 932 | auto remainingTokens = tokens; 933 | auto const &token = *remainingTokens; 934 | if( token.type != TokenType::Argument ) 935 | return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); 936 | 937 | assert( !m_ref->isFlag() ); 938 | auto valueRef = static_cast( m_ref.get() ); 939 | 940 | auto result = valueRef->setValue( remainingTokens->token ); 941 | if( !result ) 942 | return InternalParseResult( result ); 943 | else 944 | return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); 945 | } 946 | }; 947 | 948 | inline auto normaliseOpt( std::string const &optName ) -> std::string { 949 | #ifdef CLARA_PLATFORM_WINDOWS 950 | if( optName[0] == '/' ) 951 | return "-" + optName.substr( 1 ); 952 | else 953 | #endif 954 | return optName; 955 | } 956 | 957 | class Opt : public ParserRefImpl { 958 | protected: 959 | std::vector m_optNames; 960 | 961 | public: 962 | template 963 | explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} 964 | 965 | explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} 966 | 967 | template 968 | Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} 969 | 970 | template 971 | Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} 972 | 973 | auto operator[]( std::string const &optName ) -> Opt & { 974 | m_optNames.push_back( optName ); 975 | return *this; 976 | } 977 | 978 | auto getHelpColumns() const -> std::vector { 979 | std::ostringstream oss; 980 | bool first = true; 981 | for( auto const &opt : m_optNames ) { 982 | if (first) 983 | first = false; 984 | else 985 | oss << ", "; 986 | oss << opt; 987 | } 988 | if( !m_hint.empty() ) 989 | oss << " <" << m_hint << ">"; 990 | return { { oss.str(), m_description } }; 991 | } 992 | 993 | auto isMatch( std::string const &optToken ) const -> bool { 994 | auto normalisedToken = normaliseOpt( optToken ); 995 | for( auto const &name : m_optNames ) { 996 | if( normaliseOpt( name ) == normalisedToken ) 997 | return true; 998 | } 999 | return false; 1000 | } 1001 | 1002 | using ParserBase::parse; 1003 | 1004 | auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { 1005 | auto validationResult = validate(); 1006 | if( !validationResult ) 1007 | return InternalParseResult( validationResult ); 1008 | 1009 | auto remainingTokens = tokens; 1010 | if( remainingTokens && remainingTokens->type == TokenType::Option ) { 1011 | auto const &token = *remainingTokens; 1012 | if( isMatch(token.token ) ) { 1013 | if( m_ref->isFlag() ) { 1014 | auto flagRef = static_cast( m_ref.get() ); 1015 | auto result = flagRef->setFlag( true ); 1016 | if( !result ) 1017 | return InternalParseResult( result ); 1018 | if( result.value() == ParseResultType::ShortCircuitAll ) 1019 | return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); 1020 | } else { 1021 | auto valueRef = static_cast( m_ref.get() ); 1022 | ++remainingTokens; 1023 | if( !remainingTokens ) 1024 | return InternalParseResult::runtimeError( "Expected argument following " + token.token ); 1025 | auto const &argToken = *remainingTokens; 1026 | if( argToken.type != TokenType::Argument ) 1027 | return InternalParseResult::runtimeError( "Expected argument following " + token.token ); 1028 | auto result = valueRef->setValue( argToken.token ); 1029 | if( !result ) 1030 | return InternalParseResult( result ); 1031 | if( result.value() == ParseResultType::ShortCircuitAll ) 1032 | return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); 1033 | } 1034 | return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); 1035 | } 1036 | } 1037 | return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); 1038 | } 1039 | 1040 | auto validate() const -> Result override { 1041 | if( m_optNames.empty() ) 1042 | return Result::logicError( "No options supplied to Opt" ); 1043 | for( auto const &name : m_optNames ) { 1044 | if( name.empty() ) 1045 | return Result::logicError( "Option name cannot be empty" ); 1046 | #ifdef CLARA_PLATFORM_WINDOWS 1047 | if( name[0] != '-' && name[0] != '/' ) 1048 | return Result::logicError( "Option name must begin with '-' or '/'" ); 1049 | #else 1050 | if( name[0] != '-' ) 1051 | return Result::logicError( "Option name must begin with '-'" ); 1052 | #endif 1053 | } 1054 | return ParserRefImpl::validate(); 1055 | } 1056 | }; 1057 | 1058 | struct Help : Opt { 1059 | Help( bool &showHelpFlag ) 1060 | : Opt([&]( bool flag ) { 1061 | showHelpFlag = flag; 1062 | return ParserResult::ok( ParseResultType::ShortCircuitAll ); 1063 | }) 1064 | { 1065 | static_cast( *this ) 1066 | ("display usage information") 1067 | ["-?"]["-h"]["--help"] 1068 | .optional(); 1069 | } 1070 | }; 1071 | 1072 | 1073 | struct Parser : ParserBase { 1074 | 1075 | mutable ExeName m_exeName; 1076 | std::vector m_options; 1077 | std::vector m_args; 1078 | 1079 | auto operator|=( ExeName const &exeName ) -> Parser & { 1080 | m_exeName = exeName; 1081 | return *this; 1082 | } 1083 | 1084 | auto operator|=( Arg const &arg ) -> Parser & { 1085 | m_args.push_back(arg); 1086 | return *this; 1087 | } 1088 | 1089 | auto operator|=( Opt const &opt ) -> Parser & { 1090 | m_options.push_back(opt); 1091 | return *this; 1092 | } 1093 | 1094 | auto operator|=( Parser const &other ) -> Parser & { 1095 | m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); 1096 | m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); 1097 | return *this; 1098 | } 1099 | 1100 | template 1101 | auto operator|( T const &other ) const -> Parser { 1102 | return Parser( *this ) |= other; 1103 | } 1104 | 1105 | // Forward deprecated interface with '+' instead of '|' 1106 | template 1107 | auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } 1108 | template 1109 | auto operator+( T const &other ) const -> Parser { return operator|( other ); } 1110 | 1111 | auto getHelpColumns() const -> std::vector { 1112 | std::vector cols; 1113 | for (auto const &o : m_options) { 1114 | auto childCols = o.getHelpColumns(); 1115 | cols.insert( cols.end(), childCols.begin(), childCols.end() ); 1116 | } 1117 | return cols; 1118 | } 1119 | 1120 | void writeToStream( std::ostream &os ) const { 1121 | if (!m_exeName.name().empty()) { 1122 | os << "usage:\n" << " " << m_exeName.name() << " "; 1123 | bool required = true, first = true; 1124 | for( auto const &arg : m_args ) { 1125 | if (first) 1126 | first = false; 1127 | else 1128 | os << " "; 1129 | if( arg.isOptional() && required ) { 1130 | os << "["; 1131 | required = false; 1132 | } 1133 | os << "<" << arg.hint() << ">"; 1134 | if( arg.cardinality() == 0 ) 1135 | os << " ... "; 1136 | } 1137 | if( !required ) 1138 | os << "]"; 1139 | if( !m_options.empty() ) 1140 | os << " options"; 1141 | os << "\n\nwhere options are:" << std::endl; 1142 | } 1143 | 1144 | auto rows = getHelpColumns(); 1145 | size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; 1146 | size_t optWidth = 0; 1147 | for( auto const &cols : rows ) 1148 | optWidth = (std::max)(optWidth, cols.left.size() + 2); 1149 | 1150 | optWidth = (std::min)(optWidth, consoleWidth/2); 1151 | 1152 | for( auto const &cols : rows ) { 1153 | auto row = 1154 | TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + 1155 | TextFlow::Spacer(4) + 1156 | TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); 1157 | os << row << std::endl; 1158 | } 1159 | } 1160 | 1161 | friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { 1162 | parser.writeToStream( os ); 1163 | return os; 1164 | } 1165 | 1166 | auto validate() const -> Result override { 1167 | for( auto const &opt : m_options ) { 1168 | auto result = opt.validate(); 1169 | if( !result ) 1170 | return result; 1171 | } 1172 | for( auto const &arg : m_args ) { 1173 | auto result = arg.validate(); 1174 | if( !result ) 1175 | return result; 1176 | } 1177 | return Result::ok(); 1178 | } 1179 | 1180 | using ParserBase::parse; 1181 | 1182 | auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { 1183 | 1184 | struct ParserInfo { 1185 | ParserBase const* parser = nullptr; 1186 | size_t count = 0; 1187 | }; 1188 | const size_t totalParsers = m_options.size() + m_args.size(); 1189 | assert( totalParsers < 512 ); 1190 | // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do 1191 | ParserInfo parseInfos[512]; 1192 | 1193 | { 1194 | size_t i = 0; 1195 | for (auto const &opt : m_options) parseInfos[i++].parser = &opt; 1196 | for (auto const &arg : m_args) parseInfos[i++].parser = &arg; 1197 | } 1198 | 1199 | m_exeName.set( exeName ); 1200 | 1201 | auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); 1202 | while( result.value().remainingTokens() ) { 1203 | bool tokenParsed = false; 1204 | 1205 | for( size_t i = 0; i < totalParsers; ++i ) { 1206 | auto& parseInfo = parseInfos[i]; 1207 | if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { 1208 | result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); 1209 | if (!result) 1210 | return result; 1211 | if (result.value().type() != ParseResultType::NoMatch) { 1212 | tokenParsed = true; 1213 | ++parseInfo.count; 1214 | break; 1215 | } 1216 | } 1217 | } 1218 | 1219 | if( result.value().type() == ParseResultType::ShortCircuitAll ) 1220 | return result; 1221 | if( !tokenParsed ) 1222 | return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); 1223 | } 1224 | // !TBD Check missing required options 1225 | return result; 1226 | } 1227 | }; 1228 | 1229 | template 1230 | template 1231 | auto ComposableParserImpl::operator|( T const &other ) const -> Parser { 1232 | return Parser() | static_cast( *this ) | other; 1233 | } 1234 | } // namespace detail 1235 | 1236 | 1237 | // A Combined parser 1238 | using detail::Parser; 1239 | 1240 | // A parser for options 1241 | using detail::Opt; 1242 | 1243 | // A parser for arguments 1244 | using detail::Arg; 1245 | 1246 | // Wrapper for argc, argv from main() 1247 | using detail::Args; 1248 | 1249 | // Specifies the name of the executable 1250 | using detail::ExeName; 1251 | 1252 | // Convenience wrapper for option parser that specifies the help option 1253 | using detail::Help; 1254 | 1255 | // enum of result types from a parse 1256 | using detail::ParseResultType; 1257 | 1258 | // Result type for parser operation 1259 | using detail::ParserResult; 1260 | 1261 | 1262 | } // namespace clara 1263 | 1264 | #endif // CLARA_HPP_INCLUDED 1265 | -------------------------------------------------------------------------------- /src/virtual-keyboard.cpp: -------------------------------------------------------------------------------- 1 | #include "virtual-keyboard.hpp" 2 | #include "wayland-window.hpp" 3 | #include "shared/os-compatibility.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace wf 15 | { 16 | VirtualKeyboardDevice::VirtualKeyboardDevice() 17 | { 18 | auto& display = WaylandDisplay::get(); 19 | auto seat = Gdk::Display::get_default()->get_default_seat(); 20 | vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard( 21 | display.vk_manager, gdk_wayland_seat_get_wl_seat(seat->gobj())); 22 | 23 | this->send_keymap(); 24 | } 25 | 26 | void VirtualKeyboardDevice::send_keymap() 27 | { 28 | /* The keymap string is defined in keymap.tpp, it is keymap_normal */ 29 | #include "keymap.tpp" 30 | 31 | size_t keymap_size = strlen(keymap) + 1; 32 | int keymap_fd = os_create_anonymous_file(keymap_size); 33 | void *ptr = mmap(NULL, keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, 34 | keymap_fd, 0); 35 | 36 | std::strcpy((char*)ptr, keymap); 37 | zwp_virtual_keyboard_v1_keymap(vk, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, 38 | keymap_fd, keymap_size); 39 | } 40 | 41 | uint32_t get_current_time() 42 | { 43 | timespec ts; 44 | clock_gettime(CLOCK_MONOTONIC, &ts); 45 | return ts.tv_sec * 1000ll + ts.tv_nsec / 1000000ll; 46 | } 47 | 48 | void VirtualKeyboardDevice::send_key(uint32_t key, uint32_t state) const 49 | { 50 | zwp_virtual_keyboard_v1_key(vk, get_current_time(), key, state); 51 | } 52 | 53 | void VirtualKeyboardDevice::set_shift(bool shift_on) 54 | { 55 | shift_pressed_counter += (shift_on ? 1 : -1); 56 | 57 | const int modifier_shift_code = 1; 58 | zwp_virtual_keyboard_v1_modifiers(vk, 59 | shift_pressed_counter ? modifier_shift_code : 0, 0, 0, 0); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/virtual-keyboard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace wf 7 | { 8 | class VirtualKeyboardDevice 9 | { 10 | int shift_pressed_counter = 0; 11 | 12 | void send_keymap(); 13 | zwp_virtual_keyboard_v1 *vk; 14 | 15 | public: 16 | VirtualKeyboardDevice(); 17 | 18 | void set_shift(bool shift_on); 19 | void send_key(uint32_t key, uint32_t state) const; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /src/wayland-window.cpp: -------------------------------------------------------------------------------- 1 | #include "wayland-window.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace wf 15 | { 16 | // listeners 17 | static void registry_add_object(void *data, struct wl_registry *registry, 18 | uint32_t name, const char *interface, uint32_t version) 19 | { 20 | auto display = static_cast (data); 21 | 22 | if (strcmp(interface, zwf_shell_manager_v2_interface.name) == 0) 23 | { 24 | display->zwf_manager = 25 | (zwf_shell_manager_v2*) wl_registry_bind(registry, name, 26 | &zwf_shell_manager_v2_interface, std::min(version, 1u)); 27 | } 28 | 29 | if (strcmp(interface, zwp_virtual_keyboard_manager_v1_interface.name) == 0) 30 | { 31 | display->vk_manager = (zwp_virtual_keyboard_manager_v1*) 32 | wl_registry_bind(registry, name, 33 | &zwp_virtual_keyboard_manager_v1_interface, 1u); 34 | } 35 | } 36 | 37 | static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) 38 | { 39 | /* no-op */ 40 | } 41 | 42 | static struct wl_registry_listener registry_listener = 43 | { 44 | ®istry_add_object, 45 | ®istry_remove_object 46 | }; 47 | 48 | WaylandDisplay::WaylandDisplay() 49 | { 50 | auto gdk_display = gdk_display_get_default(); 51 | auto display = gdk_wayland_display_get_wl_display(gdk_display); 52 | 53 | if (!display) 54 | { 55 | std::cerr << "Failed to connect to wayland display!" 56 | << " Are you sure you are running a wayland compositor?" << std::endl; 57 | std::exit(-1); 58 | } 59 | 60 | wl_registry *registry = wl_display_get_registry(display); 61 | wl_registry_add_listener(registry, ®istry_listener, this); 62 | wl_display_dispatch(display); 63 | wl_display_roundtrip(display); 64 | 65 | if (!vk_manager) 66 | { 67 | std::cerr << "Compositor doesn't support the virtual-keyboard-v1 " 68 | << "protocol, exiting" << std::endl; 69 | std::exit(-1); 70 | } 71 | } 72 | 73 | WaylandDisplay& WaylandDisplay::get() 74 | { 75 | static WaylandDisplay instance; 76 | return instance; 77 | } 78 | 79 | int32_t WaylandWindow::check_anchor(std::string anchor) 80 | { 81 | std::transform(anchor.begin(), anchor.end(), anchor.begin(), ::tolower); 82 | 83 | int32_t parsed_anchor = -1; 84 | if (anchor.compare("top") == 0) 85 | { 86 | parsed_anchor = GTK_LAYER_SHELL_EDGE_TOP; 87 | } else if (anchor.compare("bottom") == 0) 88 | { 89 | parsed_anchor = GTK_LAYER_SHELL_EDGE_BOTTOM; 90 | } else if (anchor.compare("left") == 0) 91 | { 92 | parsed_anchor = GTK_LAYER_SHELL_EDGE_LEFT; 93 | } else if (anchor.compare("right") == 0) 94 | { 95 | parsed_anchor = GTK_LAYER_SHELL_EDGE_RIGHT; 96 | } else if (anchor.compare("pinned") == 0) 97 | { 98 | parsed_anchor = ANCHOR_PINNED_BOTTOM; 99 | } 100 | 101 | return parsed_anchor; 102 | } 103 | 104 | void WaylandWindow::init(int width, int height, std::string anchor) 105 | { 106 | gtk_layer_init_for_window(this->gobj()); 107 | gtk_layer_set_layer(this->gobj(), GTK_LAYER_SHELL_LAYER_OVERLAY); 108 | gtk_layer_set_namespace(this->gobj(), "keyboard"); 109 | gtk_layer_set_exclusive_zone(this->gobj(), -1); 110 | auto layer_anchor = check_anchor(anchor); 111 | if (layer_anchor > -1) 112 | { 113 | gtk_layer_set_anchor(this->gobj(), 114 | (GtkLayerShellEdge)layer_anchor, true); 115 | } else if (layer_anchor == ANCHOR_PINNED_BOTTOM) 116 | { 117 | gtk_layer_set_anchor(this->gobj(), 118 | GTK_LAYER_SHELL_EDGE_BOTTOM, true); 119 | gtk_layer_set_anchor(this->gobj(), 120 | GTK_LAYER_SHELL_EDGE_LEFT, true); 121 | gtk_layer_set_anchor(this->gobj(), 122 | GTK_LAYER_SHELL_EDGE_RIGHT, true); 123 | gtk_layer_auto_exclusive_zone_enable(this->gobj()); 124 | } 125 | 126 | this->set_size_request(width, height); 127 | this->show_all(); 128 | auto gdk_window = this->get_window()->gobj(); 129 | auto surface = gdk_wayland_window_get_wl_surface(gdk_window); 130 | 131 | if (surface && WaylandDisplay::get().zwf_manager) 132 | { 133 | this->wf_surface = zwf_shell_manager_v2_get_wf_surface( 134 | WaylandDisplay::get().zwf_manager, surface); 135 | } 136 | } 137 | 138 | void WaylandWindow::init_headerbar(int headerbar_size) 139 | { 140 | std::vector buttons = { 141 | &top_button, &bottom_button, &close_button 142 | }; 143 | 144 | const int button_size = 0.8 * headerbar_size; 145 | for (auto& button : buttons) 146 | { 147 | button->get_style_context()->add_class("image-button"); 148 | button->set_size_request(button_size, button_size); 149 | button->set_margin_bottom(OSK_SPACING); 150 | button->set_margin_top(OSK_SPACING); 151 | button->set_margin_left(OSK_SPACING); 152 | button->set_margin_right(OSK_SPACING); 153 | } 154 | 155 | static const std::map gtk_size_map = { 156 | {Gtk::ICON_SIZE_MENU, 16}, 157 | {Gtk::ICON_SIZE_SMALL_TOOLBAR, 16}, 158 | {Gtk::ICON_SIZE_LARGE_TOOLBAR, 24}, 159 | {Gtk::ICON_SIZE_BUTTON, 16}, 160 | {Gtk::ICON_SIZE_DND, 32}, 161 | {Gtk::ICON_SIZE_DIALOG, 48} 162 | }; 163 | 164 | Gtk::BuiltinIconSize desired_gtk_icon_size = Gtk::ICON_SIZE_MENU; 165 | for (auto [gtk_size, pixel_size] : gtk_size_map) 166 | { 167 | if (pixel_size <= button_size) 168 | { 169 | desired_gtk_icon_size = gtk_size; 170 | } 171 | } 172 | 173 | close_button.set_image_from_icon_name("window-close-symbolic", desired_gtk_icon_size); 174 | close_button.signal_clicked().connect_notify([=] () { 175 | this->get_application()->quit(); 176 | }); 177 | 178 | top_button.set_image_from_icon_name("pan-up-symbolic", desired_gtk_icon_size); 179 | top_button.signal_clicked().connect_notify([=] () { 180 | gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, true); 181 | gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, false); 182 | }); 183 | 184 | bottom_button.set_image_from_icon_name("pan-down-symbolic", desired_gtk_icon_size); 185 | bottom_button.signal_clicked().connect_notify([=] () { 186 | gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_TOP, false); 187 | gtk_layer_set_anchor(this->gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, true); 188 | }); 189 | 190 | // setup move gesture 191 | Gtk::HeaderBar bar; 192 | headerbar_box.override_background_color(bar.get_style_context()->get_background_color()); 193 | 194 | // setup headerbar layout 195 | headerbar_box.set_size_request(-1, headerbar_size); 196 | headerbar_box.pack_end(close_button, false, false); 197 | headerbar_box.pack_start(top_button, false, false); 198 | headerbar_box.pack_start(bottom_button, false, false); 199 | 200 | layout_box.pack_start(headerbar_box); 201 | layout_box.set_spacing(OSK_SPACING); 202 | this->add(layout_box); 203 | } 204 | 205 | WaylandWindow::WaylandWindow(int width, int height, std::string anchor, int headerbar_size) 206 | : Gtk::Window() 207 | { 208 | init_headerbar(headerbar_size); 209 | // setup gtk layer shell 210 | init(width, height, anchor); 211 | } 212 | 213 | void WaylandWindow::set_widget(Gtk::Widget& w) 214 | { 215 | if (current_widget) 216 | this->layout_box.remove(*current_widget); 217 | 218 | this->layout_box.pack_end(w); 219 | current_widget = &w; 220 | 221 | w.set_margin_bottom(OSK_SPACING); 222 | w.set_margin_left(OSK_SPACING); 223 | w.set_margin_right(OSK_SPACING); 224 | this->show_all(); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/wayland-window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define OSK_SPACING 8 13 | static constexpr int32_t ANCHOR_PINNED_BOTTOM = -2; 14 | 15 | namespace wf 16 | { 17 | class WaylandDisplay 18 | { 19 | WaylandDisplay(); 20 | 21 | public: 22 | static WaylandDisplay& get(); 23 | 24 | zwf_shell_manager_v2 *zwf_manager = nullptr; 25 | zwp_virtual_keyboard_manager_v1 *vk_manager = nullptr; 26 | }; 27 | 28 | class WaylandWindow : public Gtk::Window 29 | { 30 | zwf_surface_v2 *wf_surface = nullptr; 31 | 32 | Gtk::Widget* current_widget = nullptr; 33 | Gtk::Button close_button; 34 | Gtk::Button top_button; 35 | Gtk::Button bottom_button; 36 | 37 | 38 | Gtk::HBox headerbar_box; 39 | Gtk::VBox layout_box; 40 | 41 | int32_t check_anchor(std::string anchor); 42 | void init(int width, int height, std::string anchor); 43 | void init_headerbar(int headerbar_size); 44 | 45 | public: 46 | WaylandWindow(int width, int height, std::string anchor, int headerbar_size); 47 | void set_widget(Gtk::Widget& w); 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /wf-osk.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=wf-osk 3 | GenericName=Virtual Keyboard 4 | Comment=A very, very simple virtual keyboard using gtkmm, virtual-keyboard-v1 and layer-shell 5 | Keywords=screen;keyboard;virtual 6 | Exec=wf-osk 7 | StartupNotify=true 8 | Terminal=false 9 | Type=Application 10 | Icon=input-keyboard 11 | --------------------------------------------------------------------------------