• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yast / yast-country / 15022004526

14 May 2025 01:29PM UTC coverage: 42.791% (-0.2%) from 43.025%
15022004526

Pull #328

github

web-flow
Merge f0e471a76 into 17e9fd3be
Pull Request #328: Load required "setxkbmap" package on demand (bsc#1243088)

1484 of 3468 relevant lines covered (42.79%)

12.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.33
/keyboard/src/modules/Keyboard.rb
1
# Copyright (c) [2019] SUSE LLC
2
#
3
# All Rights Reserved.
4
#
5
# This program is free software; you can redistribute it and/or modify it
6
# under the terms of version 2 of the GNU General Public License as published
7
# by the Free Software Foundation.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12
# more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with this program; if not, contact SUSE LLC.
16
#
17
# To contact SUSE LLC about this file by physical or electronic mail, you may
18
# find current contact information at www.suse.com.
19

20
# File:
21
#   Keyboard.rb
22
#
23
# Module:
24
#   Keyboard
25
#
26
# Usage:
27
# ------
28
# This module provides the following data for public access via Keyboard::<var-name>.
29
#
30
#
31

32
require "yast"
1✔
33
require "shellwords"
1✔
34
require "y2keyboard/strategies/kb_strategy"
1✔
35
require "y2keyboard/strategies/systemd_strategy"
1✔
36
require "y2keyboard/keyboards"
1✔
37

38
module Yast
1✔
39
  class KeyboardClass < Module
1✔
40
    include Yast::Logger
1✔
41

42
    def main
1✔
43
      textdomain "country"
2✔
44

45
      Yast.import "Language"
2✔
46
      Yast.import "Mode"
2✔
47
      Yast.import "ProductFeatures"
2✔
48
      Yast.import "Stage"
2✔
49
      Yast.import "Report"
2✔
50

51
      # general kb strategy which is used for temporary changes only.
52
      @kb_strategy = Y2Keyboard::Strategies::KbStrategy.new
2✔
53

54
      # systemd strategy used in the installed system
55
      @systemd_strategy = Y2Keyboard::Strategies::SystemdStrategy.new
2✔
56

57
      # The keyboard currently set. E.g. "english-us"
58
      #
59
      @curr_kbd = ""
2✔
60

61
      # keyboard set on start. E.g. "english-us"
62
      #
63
      @keyboard_on_entry = ""
2✔
64

65
      # The default keyboard if set.  E.g. "english-us"
66
      #
67
      @default_kbd = ""
2✔
68

69
      # Flag indicating if the user has chosen a keyboard.
70
      # To be set from outside.
71
      #
72
      @user_decision = false
2✔
73

74
      # modify flag
75
      @modified = false
2✔
76

77
    end
78

79
    # Get the keyboard language for the given system language.
80
    #
81
    # @param [String] System language code, e.g. "en_US".
82
    # @param [String] Default keyboard language to be returned if nothing found.
83
    #                 E.g. "english-us"
84
    #
85
    # @return  The keyboard language for this language, e.g. "english-us"
86
    #               or the default value if nothing found.
87
    #
88
    def GetKeyboardForLanguage(sys_language, default_keyboard)
1✔
89
      ret = Keyboards.suggested_keyboard(sys_language) ||
6✔
90
        Language.GetLang2KeyboardMap(true)[sys_language] || # The language module has also suggestions
91
        default_keyboard
92
      log.info("Suggest keyboard #{ret} for language #{sys_language}")
6✔
93
      ret
6✔
94
    end
95

96
    # Read installed keyboard settings.
97
    def Read
1✔
98
      if !Stage.initial || Mode.live_installation
10✔
99
        curr_code = ConvertLegacyKeymapCode(@systemd_strategy.current_layout())
10✔
100
        @curr_kbd = Keyboards.alias(curr_code)
10✔
101
        if @curr_kbd.nil?
10✔
102
          log.warn "Unsupported keymap #{@systemd_strategy.current_layout()}."
2✔
103
          @curr_kbd = ""
2✔
104
        end
105
        @keyboard_on_entry = @curr_kbd
10✔
106
      end
107

108
      log.info("keyboard_on_entry: #{@keyboard_on_entry}")
10✔
109
      true
10✔
110
    end
111

112
    # was anything modified?
113
    #
114
    # @return [Boolean] true if modified
115
    def Modified
1✔
116
      @curr_kbd != @keyboard_on_entry || @modified
1✔
117
    end
118

119
    # Set to modified
120
    def SetModified
1✔
121
      @modified = true
3✔
122
    end
123

124
    # Convert a legacy keymap code to the corresponding new one. Leave it
125
    # unchanged if it's not a known legacy keymap.
126
    #
127
    # @param [String] kb_code keymap code to check and/or change
128
    #
129
    # @return [String] changed keymap code
130
    #
131
    def ConvertLegacyKeymapCode(kb_code)
1✔
132
      return kb_code unless Keyboards.legacy_code?(kb_code)
15✔
133

134
      new_kb_code = Keyboards.legacy_replacement(kb_code)
2✔
135
      log.info("Changing obsolete legacy keymap code '#{kb_code}' to '#{new_kb_code}'")
2✔
136
      Keyboard.SetModified
2✔
137
      new_kb_code
2✔
138
    end
139

140
    # Set current data into the installed system.
141
    def Save
1✔
142
      if Mode.update
1✔
143
        log.info "skipping country changes in update"
1✔
144
        return
1✔
145
      end
146
      key_code = Keyboards.code(@curr_kbd)
×
147
      log.info("Saving keyboard #{@curr_kbd}/#{key_code} to system")
×
148
      @systemd_strategy.apply_layout(key_code)
×
149
      @keyboard_on_entry = @curr_kbd
×
150
      nil
×
151
    end
152

153
    # Set the keyboard to the given keyboard language.
154
    #
155
    # @param   [String] Keyboard language e.g.  "english-us"
156
    # @return  [nil]
157
    def Set(keyboard)
1✔
158
      log.info "set to #{keyboard}"
2✔
159

160
      # Store keyboard just set.
161
      #
162
      @curr_kbd = keyboard
2✔
163

164
      # On first assignment store default keyboard.
165
      #
166
      @default_kbd = @curr_kbd if @default_kbd == "" # not yet assigned
2✔
167

168
      if !Mode.config # not in AY configuration module
2✔
169
        # Set keyboard in the running system
170
        @kb_strategy.set_layout(Keyboards.code(keyboard))
1✔
171
      end
172

173
      nil
2✔
174
    end
175

176
    # Return proposal string and set system keyboard.
177
    #
178
    # @param [Boolean] force_reset
179
    # @param [Boolean] language_changed
180
    #
181
    # @return        [String]        user readable description.
182
    #                If force_reset is true reset the module to the keyboard
183
    #                stored in default_kbd.
184
    def MakeProposal(force_reset, language_changed)
1✔
185
      log.info("force_reset: #{force_reset}")
3✔
186
      log.info("language_changed: #{language_changed}")
3✔
187

188
      if force_reset
3✔
189
        # If user wants to reset do it if a default is available.
190
        if @default_kbd != ""
×
191
          Set(@default_kbd) # reset
×
192
        end
193

194
        # Reset user_decision flag.
195
        @user_decision = false
×
196
      else
197
        # Only follow the language if the user has never actively chosen
198
        # a keyboard. The indicator for this is user_decision which is
199
        # set from outside the module.
200
        if @user_decision || Mode.update && !Stage.initial ||
3✔
201
            Mode.live_installation ||
202
            (Mode.auto && !@curr_kbd.empty?) || # AY has already set keyboard
2✔
203
            ProductFeatures.GetStringFeature("globals", "keyboard") != ""
204
          if language_changed
1✔
205
            log.info(
×
206
              "User has chosen a keyboard; not following language."
207
            )
208
          end
209
        else
210
          # User has not yet chosen a keyboard ==> follow language.
211
          local_kbd = GetKeyboardForLanguage(Language.language, "english-us")
2✔
212
          if local_kbd != ""
2✔
213
            Set(local_kbd)
2✔
214
          elsif language_changed
×
215
            log.error("Can't follow language - only retranslation")
×
216
            Set(@curr_kbd)
×
217
          end
218
        end
219
      end
220
      Keyboards.description(@curr_kbd)
3✔
221
    end
222

223
    # Get the map of translated keyboard names.
224
    #
225
    # @return        [Hash] of $[ keyboard_alias : keyboard_description, ...] for all known
226
    #                keyboards. 'keyboard_alias' is used internally in Set and Get
227
    #                functions. 'keyboard_description' is a user-readable string.
228
    #           e.g. {"arabic"=>"Arabic", "belgian"=>"Belgian",....}
229
    #
230
    def Selection
1✔
231
      lang = Keyboards.all_keyboards.map {|k| {k["alias"] => k["description"]} }
104✔
232
      Hash[*lang.collect{|h| h.to_a}.flatten]
104✔
233
    end
234

235
    # Returns all defined codes and the regarding aliases.
236
    #
237
    # @return        [Hash] of $[ keyboard_code : keyboard_alias, ...] for all known
238
    #                keyboards. 'keyboard_code' is the system key map
239
    #                functions. 'keyboard_alias' is used internally in Set and Get
240
    #           e.g. {"jp106"=>"japanese", "us"=>"english-us",....}
241
    def Codes
1✔
242
      lang = Keyboards.all_keyboards.map {|k| {k["code"] => k["alias"]} }
×
243
      Hash[*lang.collect{|h| h.to_a}.flatten]
×
244
    end
245

246
    # Return item list of keyboard items, sorted according to current language
247
    # @return [Array<Term>] Item(Id(...), String name, Boolean selected)
248
    def GetKeyboardItems
1✔
249
      ret = Builtins.maplist(Selection()) do |code, name|
1✔
250
        Item(Id(code), name, @curr_kbd == code)
51✔
251
      end
252
      Builtins.sort(ret) do |a, b|
1✔
253
        # bnc#385172: must use < instead of <=, the following means:
254
        # strcoll(x) <= strcoll(y) && strcoll(x) != strcoll(y)
255
        lsorted = Builtins.lsort(
170✔
256
          [Ops.get_string(a, 1, ""), Ops.get_string(b, 1, "")]
257
        )
258
        lsorted_r = Builtins.lsort(
170✔
259
          [Ops.get_string(b, 1, ""), Ops.get_string(a, 1, "")]
260
        )
261
        Ops.get_string(lsorted, 0, "") == Ops.get_string(a, 1, "") &&
170✔
262
          lsorted == lsorted_r
263
      end
264
    end
265

266

267
    # Set the keyboard layout according to given language
268
    # @param  [String] language e.g. "en"
269
    def SetKeyboardForLanguage(lang)
1✔
270
      lang_kbd = GetKeyboardForLanguage(lang, "english-us")
1✔
271
      log.info("language #{lang} proposed keyboard #{lang_kbd}")
1✔
272
      Set(lang_kbd) if lang_kbd != ""
1✔
273
    end
274

275
    # Set the current keyboard as default
276
    def SetKeyboardDefault
1✔
277
      log.info("SetKeyboardDefault to #{@curr_kbd}")
1✔
278
      @default_kbd = @curr_kbd
1✔
279
    end
280

281
    # AutoYaST interface function: Get the Keyboard configuration from a map.
282
    #
283
    # @param [Hash] settings; imported map with the content of either the
284
    #       'keyboard' or the 'language' section
285
    # @param [:keyboard, :language] syntax ; format of settings: if :language, the
286
    #       data for Language.Import
287
    # @return success
288
    def Import(settings, syntax = :keyboard)
1✔
289
      settings = deep_copy(settings)
5✔
290
      # Read was not called -> do the init
291
      Read() unless Mode.config # not in AY configuration module
5✔
292

293
      keyboard = @curr_kbd
5✔
294

295
      case syntax
5✔
296
      when :keyboard
297
        keyboard = settings["keymap"] if settings["keymap"]
4✔
298
      when :language
299
        keyboard = GetKeyboardForLanguage(settings["language"], keyboard)
1✔
300
      end
301

302
      keyboard = ConvertLegacyKeymapCode(keyboard)
5✔
303

304
      # Checking if the keymap exists. Either it is the real keymap name
305
      # or an alias.
306
      if !Keyboards.code(keyboard)
5✔
307
        # Checking if it a real keymap name
308
        checked_keyboard = keyboard
3✔
309
        keyboard = Keyboards.alias(checked_keyboard)
3✔
310
        if !keyboard
3✔
311
          # TRANSLATORS: "%s" is the keymap name
312
          Report.Warning(_("Cannot find keymap: %s. Taking default one.") % checked_keyboard)
1✔
313
          return false
1✔
314
        end
315
      end
316

317
      # Set it with the keyboard alias name
318
      Set(keyboard)
4✔
319
      true
4✔
320
    end
321

322
    # AutoYaST interface function: Return the Keyboard configuration as a map.
323
    # @return [Hash] with the settings
324
    def Export
1✔
325
      ret = {}
×
326

327
      if @curr_kbd == Keyboard.GetKeyboardForLanguage(Language.language,
×
328
           "english-us")
329
        log.info("keymap #{@curr_kbd} is the default of language "\
×
330
                 "#{Language.language} --> no export")
331
      else
332
        ret["keymap"] = @curr_kbd
×
333
      end
334
      deep_copy(ret)
×
335
    end
336

337
    # AutoYaST interface function: Return the summary of Keyboard configuration as a map.
338
    # @return summary string (html)
339
    def Summary
1✔
340
      Yast.import "HTML"
×
341

342
      ret = [
343
        # summary label
344
        _("Current Keyboard Layout: %s" % Keyboards.description(@curr_kbd))
×
345
      ]
346
      HTML.List(ret)
×
347
    end
348

349
    # Returning current keyboard
350
    # @return [String] keyboard name e.g. "english-us"
351
    def current_kbd
1✔
352
      self.Read if @curr_kbd.empty? && !Mode.config
5✔
353
      @curr_kbd
5✔
354
    end
355

356
    publish :variable => :keyboard_on_entry, :type => "string"
1✔
357
    publish :variable => :default_kbd, :type => "string"
1✔
358
    publish :variable => :user_decision, :type => "boolean"
1✔
359
    publish :function => :current_kbd, :type => "string ()"
1✔
360
    publish :function => :Set, :type => "void (string)"
1✔
361
    publish :function => :GetKeyboardForLanguage, :type => "string (string, string)"
1✔
362
    publish :function => :Read, :type => "boolean ()"
1✔
363
    publish :function => :Modified, :type => "boolean ()"
1✔
364
    publish :function => :SetModified, :type => "void (boolean)"
1✔
365
    publish :function => :Save, :type => "void ()"
1✔
366
    publish :function => :MakeProposal, :type => "string (boolean, boolean)"
1✔
367
    publish :function => :Selection, :type => "map <string, string> ()"
1✔
368
    publish :function => :codes, type: "map <string,string> ()"
1✔
369
    publish :function => :GetKeyboardItems, :type => "list <term> ()"
1✔
370
    publish :function => :SetKeyboardForLanguage, :type => "void (string)"
1✔
371
    publish :function => :SetKeyboardDefault, :type => "void ()"
1✔
372
    publish :function => :Import, :type => "boolean (map, ...)"
1✔
373
    publish :function => :Export, :type => "map ()"
1✔
374
    publish :function => :Summary, :type => "string ()"
1✔
375

376
  end
377

378
  Keyboard = KeyboardClass.new
1✔
379
  Keyboard.main
1✔
380
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc