// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:path/path.dart' as path; import 'base_code_gen.dart'; import 'constants.dart'; import 'logical_key_data.dart'; import 'physical_key_data.dart'; import 'utils.dart'; /// Generates the key mapping for GTK, based on the information in the key /// data structure given to it. class GtkCodeGenerator extends PlatformCodeGenerator { GtkCodeGenerator( super.keyData, super.logicalData, String modifierBitMapping, String lockBitMapping, this._layoutGoals, ) : _modifierBitMapping = parseMapOfListOfString(modifierBitMapping), _lockBitMapping = parseMapOfListOfString(lockBitMapping); /// This generates the map of XKB scan codes to Flutter physical keys. String get _xkbScanCodeMap { final OutputLines<int> lines = OutputLines<int>('GTK scancode map'); for (final PhysicalKeyEntry entry in keyData.entries) { if (entry.xKbScanCode != null) { lines.add(entry.xKbScanCode!, ' {${toHex(entry.xKbScanCode)}, ${toHex(entry.usbHidCode)}}, // ${entry.constantName}'); } } return lines.sortedJoin().trimRight(); } /// This generates the map of GTK keyval codes to Flutter logical keys. String get _gtkKeyvalCodeMap { final OutputLines<int> lines = OutputLines<int>('GTK keyval map'); for (final LogicalKeyEntry entry in logicalData.entries) { zipStrict(entry.gtkValues, entry.gtkNames, (int value, String name) { lines.add(value, ' {${toHex(value)}, ${toHex(entry.value, digits: 11)}}, // $name'); }); } return lines.sortedJoin().trimRight(); } static String constructMapFromModToKeys( Map<String, List<String>> source, PhysicalKeyData physicalData, LogicalKeyData logicalData, String debugFunctionName, ) { final StringBuffer result = StringBuffer(); source.forEach((String modifierBitName, List<String> keyNames) { assert(keyNames.length == 2 || keyNames.length == 3); final String primaryPhysicalName = keyNames[0]; final String primaryLogicalName = keyNames[1]; final String? secondaryLogicalName = keyNames.length == 3 ? keyNames[2] : null; final PhysicalKeyEntry primaryPhysical = physicalData.entryByName(primaryPhysicalName); final LogicalKeyEntry primaryLogical = logicalData.entryByName(primaryLogicalName); final LogicalKeyEntry? secondaryLogical = secondaryLogicalName == null ? null : logicalData.entryByName(secondaryLogicalName); if (secondaryLogical == null && secondaryLogicalName != null) { print('Unrecognized secondary logical key $secondaryLogicalName specified for $debugFunctionName.'); return; } final String pad = secondaryLogical == null ? '' : ' '; result.writeln(''' data = g_new(FlKeyEmbedderCheckedKey, 1); g_hash_table_insert(table, GUINT_TO_POINTER(GDK_${modifierBitName}_MASK), data); data->is_caps_lock = ${primaryPhysicalName == 'CapsLock' ? 'true' : 'false'}; data->primary_physical_key = ${toHex(primaryPhysical.usbHidCode, digits: 9)};$pad // ${primaryPhysical.constantName} data->primary_logical_key = ${toHex(primaryLogical.value, digits: 11)};$pad // ${primaryLogical.constantName}'''); if (secondaryLogical != null) { result.writeln(''' data->secondary_logical_key = ${toHex(secondaryLogical.value, digits: 11)}; // ${secondaryLogical.constantName}'''); } }); return result.toString().trimRight(); } String get _gtkModifierBitMap { return constructMapFromModToKeys(_modifierBitMapping, keyData, logicalData, 'gtkModifierBitMap'); } final Map<String, List<String>> _modifierBitMapping; String get _gtkModeBitMap { return constructMapFromModToKeys(_lockBitMapping, keyData, logicalData, 'gtkModeBitMap'); } final Map<String, List<String>> _lockBitMapping; final Map<String, bool> _layoutGoals; String get _layoutGoalsString { final OutputLines<int> lines = OutputLines<int>('GTK layout goals'); _layoutGoals.forEach((String name, bool mandatory) { final PhysicalKeyEntry physicalEntry = keyData.entryByName(name); final LogicalKeyEntry logicalEntry = logicalData.entryByName(name); final String line = 'LayoutGoal{' '${toHex(physicalEntry.xKbScanCode, digits: 2)}, ' '${toHex(logicalEntry.value, digits: 2)}, ' '${mandatory ? 'true' : 'false'}' '},'; lines.add(logicalEntry.value, ' ${line.padRight(39)}' '// ${logicalEntry.name}'); }); return lines.sortedJoin().trimRight(); } /// This generates the mask values for the part of a key code that defines its plane. String get _maskConstants { final StringBuffer buffer = StringBuffer(); const List<MaskConstant> maskConstants = <MaskConstant>[ kValueMask, kUnicodePlane, kGtkPlane, ]; for (final MaskConstant constant in maskConstants) { buffer.writeln('const uint64_t k${constant.upperCamelName} = ${toHex(constant.value, digits: 11)};'); } return buffer.toString().trimRight(); } @override String get templatePath => path.join(dataRoot, 'gtk_key_mapping_cc.tmpl'); @override String outputPath(String platform) => path.join(PlatformCodeGenerator.engineRoot, 'shell', 'platform', 'linux', 'key_mapping.g.cc'); @override Map<String, String> mappings() { return <String, String>{ 'XKB_SCAN_CODE_MAP': _xkbScanCodeMap, 'GTK_KEYVAL_CODE_MAP': _gtkKeyvalCodeMap, 'GTK_MODIFIER_BIT_MAP': _gtkModifierBitMap, 'GTK_MODE_BIT_MAP': _gtkModeBitMap, 'MASK_CONSTANTS': _maskConstants, 'LAYOUT_GOALS': _layoutGoalsString, }; } }