generate.pl (8669B)
1#!/usr/bin/env perl 2# 3# Licensed to the Apache Software Foundation (ASF) under one 4# or more contributor license agreements. See the NOTICE file 5# distributed with this work for additional information 6# regarding copyright ownership. The ASF licenses this file 7# to you under the Apache License, Version 2.0 (the 8# "License"); you may not use this file except in compliance 9# with the License. You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, 14# software distributed under the License is distributed on an 15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16# KIND, either express or implied. See the License for the 17# specific language governing permissions and limitations 18# under the License. 19# 20 21# 22# generate.pl 23# 24# Parse .keymap files, producing corresponding .c files that can be included 25# into the RDP plugin source. 26# 27 28# We need at least Perl 5.8 for Unicode's sake 29use 5.008; 30 31sub keymap_symbol { 32 my $name = shift; 33 $name =~ s/-/_/g; 34 return 'guac_rdp_keymap_' . $name; 35} 36 37# 38# _generated_keymaps.c 39# 40 41my @keymaps = (); 42 43open OUTPUT, ">", "_generated_keymaps.c"; 44print OUTPUT 45 '#include "config.h"' . "\n" 46 . '#include "keymap.h"' . "\n" 47 . '#include <freerdp/input.h>' . "\n" 48 . '#include <freerdp/locale/keyboard.h>' . "\n" 49 . "\n" 50 . '#include <stddef.h>' . "\n" 51 . "\n"; 52 53for my $filename (@ARGV) { 54 55 my $content = ""; 56 my $parent = ""; 57 my $layout_name = ""; 58 my $freerdp = ""; 59 60 # Parse file 61 open INPUT, '<', "$filename"; 62 binmode INPUT, ":encoding(utf8)"; 63 while (<INPUT>) { 64 65 chomp; 66 67 # Comments 68 if (m/^\s*#/) {} 69 70 # Name 71 elsif ((my $name) = m/^\s*name\s+"(.*)"\s*(?:#.*)?$/) { 72 $layout_name = $name; 73 } 74 75 # Parent map 76 elsif ((my $name) = m/^\s*parent\s+"(.*)"\s*(?:#.*)?$/) { 77 $parent = keymap_symbol($name); 78 } 79 80 # FreeRDP equiv 81 elsif ((my $name) = m/^\s*freerdp\s+"(.*)"\s*(?:#.*)?$/) { 82 $freerdp = $name; 83 } 84 85 # Map 86 elsif ((my $range, my $onto) = 87 m/^\s*map\s+([^~]*)\s+~\s+(".*"|0x[0-9A-Fa-f]+)\s*(?:#.*)?$/) { 88 89 my @keysyms = (); 90 my @scancodes = (); 91 92 my $ext_flags = 0; 93 my $set_shift = 0; 94 my $set_altgr = 0; 95 my $set_caps = 0; 96 my $set_num = 0; 97 98 my $clear_shift = 0; 99 my $clear_altgr = 0; 100 my $clear_caps = 0; 101 my $clear_num = 0; 102 103 # Parse ranges and options 104 foreach $_ (split(/\s+/, $range)) { 105 106 # Set option/modifier 107 if ((my $opt) = m/^\+([a-z]+)$/) { 108 if ($opt eq "shift") { $set_shift = 1; } 109 elsif ($opt eq "altgr") { $set_altgr = 1; } 110 elsif ($opt eq "caps") { $set_caps = 1; } 111 elsif ($opt eq "num") { $set_num = 1; } 112 elsif ($opt eq "ext") { $ext_flags = 1; } 113 else { 114 die "$filename: $.: ERROR: " 115 . "Invalid set option\n"; 116 } 117 } 118 119 # Clear option/modifier 120 elsif ((my $opt) = m/^-([a-z]+)$/) { 121 if ($opt eq "shift") { $clear_shift = 1; } 122 elsif ($opt eq "altgr") { $clear_altgr = 1; } 123 elsif ($opt eq "caps") { $clear_caps = 1; } 124 elsif ($opt eq "num") { $clear_num = 1; } 125 else { 126 die "$filename: $.: ERROR: " 127 . "Invalid clear option\n"; 128 } 129 } 130 131 # Single scancode 132 elsif ((my $scancode) = m/^(0x[0-9A-Fa-f]+)$/) { 133 $scancodes[++$#scancodes] = hex($scancode); 134 } 135 136 # Range of scancodes 137 elsif ((my $start, my $end) = 138 m/^(0x[0-9A-Fa-f]+)\.\.(0x[0-9A-Fa-f]+)$/) { 139 for (my $i=hex($start); $i<=hex($end); $i++) { 140 $scancodes[++$#scancodes] = $i; 141 } 142 } 143 144 # Invalid token 145 else { 146 die "$filename: $.: ERROR: " 147 . "Invalid token\n"; 148 } 149 150 } 151 152 # Parse onto 153 if ($onto =~ m/^0x/) { 154 $keysyms[0] = hex($onto); 155 } 156 else { 157 foreach my $char (split('', 158 substr($onto, 1, length($onto)-2))) { 159 my $codepoint = ord($char); 160 if ($codepoint >= 0x0100) { 161 $keysyms[++$#keysyms] = 0x01000000 | $codepoint; 162 } 163 else { 164 $keysyms[++$#keysyms] = $codepoint; 165 } 166 } 167 } 168 169 # Check mapping 170 if ($#keysyms != $#scancodes) { 171 die "$filename: $.: ERROR: " 172 . "Keysym and scancode range lengths differ\n"; 173 } 174 175 # Write keysym/scancode pairs 176 for (my $i=0; $i<=$#keysyms; $i++) { 177 178 $content .= " {" 179 . " .keysym = " . $keysyms[$i] . "," 180 . " .scancode = " . $scancodes[$i]; 181 182 # Modifiers that must be active 183 $content .= ", .set_modifiers = 0"; 184 $content .= " | GUAC_RDP_KEYMAP_MODIFIER_SHIFT" if $set_shift; 185 $content .= " | GUAC_RDP_KEYMAP_MODIFIER_ALTGR" if $set_altgr; 186 187 # Modifiers that must be inactive 188 $content .= ", .clear_modifiers = 0"; 189 $content .= " | GUAC_RDP_KEYMAP_MODIFIER_SHIFT" if $clear_shift; 190 $content .= " | GUAC_RDP_KEYMAP_MODIFIER_ALTGR" if $clear_altgr; 191 192 # Locks that must be set 193 $content .= ", .set_locks = 0"; 194 $content .= " | KBD_SYNC_NUM_LOCK" if $set_num; 195 $content .= " | KBD_SYNC_CAPS_LOCK" if $set_caps; 196 197 # Locks that must NOT be set 198 $content .= ", .clear_locks = 0"; 199 $content .= " | KBD_SYNC_NUM_LOCK" if $clear_num; 200 $content .= " | KBD_SYNC_CAPS_LOCK" if $clear_caps; 201 202 # Flags 203 if ($ext_flags) { 204 $content .= ", .flags = KBD_FLAGS_EXTENDED"; 205 } 206 207 $content .= " },\n"; 208 209 } 210 211 } 212 213 # Invalid token 214 elsif (m/\S/) { 215 die "$filename: $.: ERROR: " 216 . "Invalid token\n"; 217 } 218 219 } 220 close INPUT; 221 222 # Header 223 my $sym = keymap_symbol($layout_name); 224 print OUTPUT 225 "\n" 226 . '/* Autogenerated from ' . $filename . ' */' . "\n" 227 . 'static guac_rdp_keysym_desc __' . $sym . '[] = {' . "\n" 228 . $content 229 . ' {0}' . "\n" 230 . '};' . "\n"; 231 232 # Desc header 233 print OUTPUT "\n" 234 . 'static const guac_rdp_keymap ' . $sym . ' = { ' . "\n"; 235 236 # Layout name 237 print OUTPUT " .name = \"$layout_name\",\n"; 238 239 # Parent layout (if any) 240 if ($parent) { 241 print OUTPUT " .parent = &$parent,\n"; 242 } 243 244 # FreeRDP layout (if any) 245 if ($freerdp) { 246 print OUTPUT " .freerdp_keyboard_layout = $freerdp,\n"; 247 } 248 249 # Desc footer 250 print OUTPUT 251 ' .mapping = __' . $sym . "\n" 252 . '};' . "\n"; 253 254 $keymaps[++$#keymaps] = $sym; 255 print STDERR "Added: $layout_name\n"; 256 257} 258 259print OUTPUT "\n" 260 . 'const guac_rdp_keymap* GUAC_KEYMAPS[] = {' . "\n"; 261 262foreach my $keymap (@keymaps) { 263 print OUTPUT " &$keymap,\n"; 264} 265print OUTPUT 266 ' NULL' . "\n" 267 . '};' . "\n"; 268 269close OUTPUT; 270