From 603be104f41be750a30fedf223f371fc5559f292 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Sat, 11 Dec 2021 22:41:46 -0800 Subject: [PATCH] Full scratch --- .gitignore | 7 - Makefile | 35 ----- README.md | 120 ---------------- build_config.rb | 26 ---- examples/asus-keyboard.rb | 5 - examples/emacs_like.rb | 31 ----- examples/launcher.rb | 3 - examples/macos.rb | 9 -- examples/multi_config.rb | 3 - examples/slack.rb | 7 - examples/systemd/xremap.service | 19 --- examples/window_switcher.rb | 8 -- mrbgem.rake | 14 -- mrblib/xremap/active_window.rb | 26 ---- mrblib/xremap/config.rb | 63 --------- mrblib/xremap/config_dsl.rb | 65 --------- mrblib/xremap/event_handler.rb | 39 ------ mrblib/xremap/grab_manager.rb | 25 ---- mrblib/xremap/key_expression.rb | 49 ------- mrblib/xremap/key_remap_compiler.rb | 55 -------- src/display.c | 45 ------ src/x11_constants.c | 17 --- src/xlib_wrapper.c | 206 ---------------------------- src/xremap_gem.c | 21 --- tools/xremap/config.c | 17 --- tools/xremap/event_handler.c | 41 ------ tools/xremap/main.c | 96 ------------- tools/xremap/xremap.h | 11 -- 28 files changed, 1063 deletions(-) delete mode 100644 .gitignore delete mode 100644 Makefile delete mode 100644 README.md delete mode 100644 build_config.rb delete mode 100644 examples/asus-keyboard.rb delete mode 100644 examples/emacs_like.rb delete mode 100644 examples/launcher.rb delete mode 100644 examples/macos.rb delete mode 100644 examples/multi_config.rb delete mode 100644 examples/slack.rb delete mode 100644 examples/systemd/xremap.service delete mode 100644 examples/window_switcher.rb delete mode 100644 mrbgem.rake delete mode 100644 mrblib/xremap/active_window.rb delete mode 100644 mrblib/xremap/config.rb delete mode 100644 mrblib/xremap/config_dsl.rb delete mode 100644 mrblib/xremap/event_handler.rb delete mode 100644 mrblib/xremap/grab_manager.rb delete mode 100644 mrblib/xremap/key_expression.rb delete mode 100644 mrblib/xremap/key_remap_compiler.rb delete mode 100644 src/display.c delete mode 100644 src/x11_constants.c delete mode 100644 src/xlib_wrapper.c delete mode 100644 src/xremap_gem.c delete mode 100644 tools/xremap/config.c delete mode 100644 tools/xremap/event_handler.c delete mode 100644 tools/xremap/main.c delete mode 100644 tools/xremap/xremap.h diff --git a/.gitignore b/.gitignore deleted file mode 100644 index b603682..0000000 --- a/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -/xremap -/mruby -tags -TAGS -src/x11_constants_keysymdef.inc -src/x11_constants_X.inc -src/x11_constants_XF86keysym.inc diff --git a/Makefile b/Makefile deleted file mode 100644 index b449674..0000000 --- a/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -current_dir := $(shell pwd) -CSRCS := $(wildcard tools/xremap/*.[ch]) -MRBSRCS := $(wildcard mrblib/xremap/*.rb) -MRBCSRCS := $(wildcard src/*.[ch]) -# Using master to apply https://github.com/mruby/mruby/pull/3192 -REVISION=0ff3ae1fbaed62010c54c43235e29cdc85da2f78 -DESTDIR := /usr/local/bin -.PHONY: all clean install - -all: xremap - -clean: - rm -rf mruby/build/host src/*.inc - -install: xremap - mv xremap $(DESTDIR)/xremap - -xremap: mruby/build/host/bin/xremap - cp mruby/build/host/bin/xremap xremap - -mruby: - git clone https://github.com/mruby/mruby - git -C mruby reset --hard $(REVISION) - -src/x11_constants_keysymdef.inc: - cat /usr/include/X11/keysymdef.h | ruby -e 'puts STDIN.read.split("\n").select {|l| l.match(/\A(#define XK_|#ifdef|#endif)/) }.map{|l| l.match(/\A#define XK_/) ? %Q[ define_x11_const(#{l.split(" ")[1]});] : l }.join("\n")' > src/x11_constants_keysymdef.inc - -src/x11_constants_X.inc: - cat /usr/include/X11/X.h | ruby -e 'puts STDIN.read.split("\n").select {|l| l.start_with?("#")}[2..-2].map{|l| l.start_with?("#define") ? %Q[ define_x11_const(#{l.split(" ")[1]});] : l}' > src/x11_constants_X.inc - -src/x11_constants_XF86keysym.inc: - cat /usr/include/X11/XF86keysym.h | ruby -e 'puts STDIN.read.split("\n").select {|l| l.match(/\A(#define XF86XK_)/) }.map{|l| l.match(/\A#define XF*86XK_/) ? %Q[ define_x11_const(#{l.split(" ")[1]});] : l }.join("\n")' > src/x11_constants_XF86keysym.inc - -mruby/build/host/bin/xremap: mruby build_config.rb src/x11_constants_keysymdef.inc src/x11_constants_X.inc src/x11_constants_XF86keysym.inc $(CSRCS) $(MRBSRCS) $(MRBCSRCS) - cd mruby && MRUBY_CONFIG="$(current_dir)/build_config.rb" make diff --git a/README.md b/README.md deleted file mode 100644 index 84021de..0000000 --- a/README.md +++ /dev/null @@ -1,120 +0,0 @@ -# xremap - -Dynamic key remapper for X Window System - -## Description - -xremap is a key remapper for X Window System. -With xremap's Ruby DSL, you can simply write configuration of key bindings. - -```rb -remap 'C-b', to: 'Left' -``` - -And you can configure application-specific key bindings, -which is dynamically applied based on a current window. - -```rb -window class_only: 'slack' do - remap 'Alt-k', to: 'Alt-Up' - remap 'Alt-j', to: 'Alt-Down' -end -``` - -While xremap's configuration is written in Ruby, you can run xremap without Ruby installation -because it embeds mruby to evaluate configuration. - -## Installation - -### Build dependencies - -- ruby -- bison -- libx11-dev - -While ruby is not runtime dependency for xremap, mruby embedded in xremap requires ruby to build. - -### From source code - -```bash -$ git clone https://github.com/k0kubun/xremap -$ cd xremap -$ make -$ sudo make install # or `make DESTDIR=~/bin install` -``` - -## Usage - -``` -$ xremap /path/to/config -``` - -See [examples](./examples) to write config file. - -### Emacs-like bindings - -```rb -window class_not: 'urxvt' do - remap 'C-b', to: 'Left' - remap 'C-f', to: 'Right' - remap 'C-p', to: 'Up' - remap 'C-n', to: 'Down' - - remap 'M-b', to: 'Ctrl-Left' - remap 'M-f', to: 'Ctrl-Right' - - remap 'C-a', to: 'Home' - remap 'C-e', to: 'End' - - remap 'C-k', to: ['Shift-End', 'Ctrl-x'] - - remap 'C-d', to: 'Delete' - remap 'M-d', to: 'Ctrl-Delete' -end -``` - -### Simulate macOS's command key - -Following configuration works fine with above Emacs-like bindings. - -```rb -%w[a z x c v w t].each do |key| - remap "Alt-#{key}", to: "C-#{key}" -end -``` - -### Application launcher - -You can start an application by a shortcut key. -See [examples/window\_switcher](examples/window_switcher.rb) too. - -```rb -remap 'C-o', to: execute('nocturn') -remap 'C-u', to: execute('google-chrome-stable') -remap 'C-h', to: execute('urxvt') -``` - -### Application-specific key bindings - -See xremap's stdout to find a window class name of your application. - -```rb -window class_only: 'slack' do - remap 'Alt-n', to: 'Ctrl-k' - remap 'Alt-k', to: 'Alt-Up' - remap 'Alt-j', to: 'Alt-Down' - remap 'Ctrl-Alt-k', to: 'Alt-Shift-Up' - remap 'Ctrl-Alt-j', to: 'Alt-Shift-Down' -end -``` - -## Note - -xremap is designed to have similar functionality with -[Karabiner](https://github.com/tekezo/Karabiner) and -[karabiner-dsl](https://github.com/k0kubun/karabiner-dsl) -for Linux environments. - -## Author - -Takashi Kokubun diff --git a/build_config.rb b/build_config.rb deleted file mode 100644 index cad29ee..0000000 --- a/build_config.rb +++ /dev/null @@ -1,26 +0,0 @@ -MRuby::Build.new do |conf| - toolchain :gcc - - conf.gembox 'default' - conf.gem File.expand_path(File.dirname(__FILE__)) - - conf.instance_eval do - # Allow showing backtrace. - @mrbc.compile_options += ' -g' - end - - conf.cc do |cc| - cc.include_paths += %w(/opt/X11/include) - - # Never support Visual C++. - # https://github.com/mruby/mruby/blob/1.2.0/CONTRIBUTING.md#comply-with-c99-isoiec-98991999 - (cc.flags.first.is_a?(String) ? cc.flags : cc.flags.first).reject! do |flag| - flag == '-Wdeclaration-after-statement' - end - end - - conf.linker do |linker| - linker.libraries += %w(X11) - linker.library_paths += %w(/opt/X11/lib) - end -end diff --git a/examples/asus-keyboard.rb b/examples/asus-keyboard.rb deleted file mode 100644 index d904f42..0000000 --- a/examples/asus-keyboard.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Remap Asus ROG keyboards to Fn+Arrow keys to more useful bindings -remap 'XF86XK_AudioPrev', to: 'Home' -remap 'XF86XK_AudioNext', to: 'End' -remap 'XF86XK_AudioPlay', to: 'Page_Down' -remap 'XF86XK_AudioStop', to: 'Page_Up' diff --git a/examples/emacs_like.rb b/examples/emacs_like.rb deleted file mode 100644 index 26aeac4..0000000 --- a/examples/emacs_like.rb +++ /dev/null @@ -1,31 +0,0 @@ -window class_not: 'urxvt' do - # emacs-like bindings - remap 'C-b', to: 'Left' - remap 'C-f', to: 'Right' - remap 'C-p', to: 'Up' - remap 'C-n', to: 'Down' - - remap 'M-b', to: 'Ctrl-Left' - remap 'M-f', to: 'Ctrl-Right' - - remap 'C-a', to: 'Home' - remap 'C-e', to: 'End' - - remap 'C-k', to: ['Shift-End', 'Ctrl-x'] - - remap 'C-d', to: 'Delete' - remap 'M-d', to: 'Ctrl-Delete' - - remap 'M-w', to: 'Ctrl-c' - remap 'C-y', to: 'Ctrl-v' - remap 'C-w', to: 'Ctrl-x' - - remap 'C-v', to: 'Page_Down' - remap 'M-v', to: 'Page_Up' - - remap 'C-s', to: 'Ctrl-f' - - # actually these are vim insert mode bindings, but compatible with shell - remap 'C-u', to: ['Shift-Home', 'Ctrl-x'] - remap 'C-w', to: ['Ctrl-Shift-Left', 'Ctrl-x'] -end diff --git a/examples/launcher.rb b/examples/launcher.rb deleted file mode 100644 index dbf1603..0000000 --- a/examples/launcher.rb +++ /dev/null @@ -1,3 +0,0 @@ -remap 'C-o', to: execute('nocturn') -remap 'C-u', to: execute('google-chrome-stable') -remap 'C-h', to: execute('urxvt') diff --git a/examples/macos.rb b/examples/macos.rb deleted file mode 100644 index bdeeaa4..0000000 --- a/examples/macos.rb +++ /dev/null @@ -1,9 +0,0 @@ -%w[a z x c v w t].each do |key| - remap "Alt-#{key}", to: "C-#{key}" -end - -window class_only: 'google-chrome' do - %w[f l].each do |key| - remap "Alt-#{key}", to: "C-#{key}" - end -end diff --git a/examples/multi_config.rb b/examples/multi_config.rb deleted file mode 100644 index 7e6a0d9..0000000 --- a/examples/multi_config.rb +++ /dev/null @@ -1,3 +0,0 @@ -include_config 'emacs_like' -include_config 'slack' -include_config 'window_switcher' diff --git a/examples/slack.rb b/examples/slack.rb deleted file mode 100644 index b421785..0000000 --- a/examples/slack.rb +++ /dev/null @@ -1,7 +0,0 @@ -window class_only: 'slack' do - remap 'Alt-n', to: 'Ctrl-k' - remap 'Alt-k', to: 'Alt-Up' - remap 'Alt-j', to: 'Alt-Down' - remap 'Ctrl-Alt-k', to: 'Alt-Shift-Up' - remap 'Ctrl-Alt-j', to: 'Alt-Shift-Down' -end diff --git a/examples/systemd/xremap.service b/examples/systemd/xremap.service deleted file mode 100644 index 4e39cb6..0000000 --- a/examples/systemd/xremap.service +++ /dev/null @@ -1,19 +0,0 @@ -# 1. Copy this to ~/.config/systemd/user/xremap.service -# 2. systemctl --user enable xremap -# -# Note that you need to set proper $DISPLAY on your environment. - -[Unit] -Description=xremap - -[Service] -KillMode=process -ExecStart=/usr/bin/xremap /home/k0kubun/.xremap -Type=simple -Restart=always - -# Update DISPLAY to be the same as `echo $DISPLAY` on your graphical terminal. -Environment=DISPLAY=:1 - -[Install] -WantedBy=default.target diff --git a/examples/window_switcher.rb b/examples/window_switcher.rb deleted file mode 100644 index 7dd07a2..0000000 --- a/examples/window_switcher.rb +++ /dev/null @@ -1,8 +0,0 @@ -define :activate do |wm_class, command| - execute("wmctrl -x -a #{wm_class.shellescape} || #{command.shellescape}") -end - -# Check WM_CLASS by wmctrl -x -l -remap 'C-o', to: activate('nocturn.Nocturn', '/usr/share/nocturn/Nocturn') -remap 'C-u', to: activate('google-chrome.Google-chrome', '/opt/google/chrome/chrome') -remap 'C-h', to: activate('urxvt.URxvt', 'urxvt') diff --git a/mrbgem.rake b/mrbgem.rake deleted file mode 100644 index 4341b39..0000000 --- a/mrbgem.rake +++ /dev/null @@ -1,14 +0,0 @@ -MRuby::Gem::Specification.new('xremap') do |spec| - spec.license = 'MIT' - spec.author = 'Takashi Kokubun' - spec.summary = 'Dynamic key remapper for X Window System' - spec.bins = ['xremap'] - - spec.add_dependency 'mruby-eval', core: 'mruby-eval' - - spec.add_dependency 'mruby-env', mgem: 'mruby-env' - spec.add_dependency 'mruby-io', mgem: 'mruby-io' - spec.add_dependency 'mruby-process', mgem: 'mruby-process' - spec.add_dependency 'mruby-onig-regexp', mgem: 'mruby-onig-regexp' - spec.add_dependency 'mruby-shellwords', mgem: 'mruby-shellwords' -end diff --git a/mrblib/xremap/active_window.rb b/mrblib/xremap/active_window.rb deleted file mode 100644 index 6dff857..0000000 --- a/mrblib/xremap/active_window.rb +++ /dev/null @@ -1,26 +0,0 @@ -module Xremap - class ActiveWindow - # @param [Fixnum] current_window - attr_reader :current_window - - # @param [Xremap::Display] display - def initialize(display) - @display = display - @current_window = fetch_active_window - end - - def changed? - next_window = fetch_active_window - @current_window != next_window - ensure - @current_window = next_window - end - - private - - def fetch_active_window - sleep ENV.fetch('XREMAP_DELAY', '0.1').to_f - XlibWrapper.fetch_active_window(@display) - end - end -end diff --git a/mrblib/xremap/config.rb b/mrblib/xremap/config.rb deleted file mode 100644 index dbdc5b5..0000000 --- a/mrblib/xremap/config.rb +++ /dev/null @@ -1,63 +0,0 @@ -module Xremap - class Config - # FIXME: :to_keys should be :to_actions, and Key and Execute should be adapted. - Remap = Struct.new(:from_key, :to_keys) - Execute = Struct.new(:command, :action) - - class Key < Struct.new(:keysym, :modifier, :action) - def initialize(*) - super - self.action ||= :input - end - end - - class Window < Struct.new(:class_only, :class_not) - def initialize(*) - super - self.class_only = self.class_only ? Array(self.class_only) : [] - self.class_not = self.class_not ? Array(self.class_not) : [] - end - end - - AnyWindow = Window.new - - # @param [String] filename - def self.load(filename) - unless File.exist?(filename) - raise "Config file does not exist!: #{filename.inspect}" - exit 1 - end - - config_dir = File.dirname(File.expand_path(filename)) - config = self.new(config_dir) - ConfigDSL.new(config).instance_eval(File.read(filename)) - config - end - - attr_reader :remaps_by_window, :config_dir - - def initialize(config_dir) - @remaps_by_window = Hash.new { |h, k| h[k] = [] } - @config_dir = config_dir - end - - def remaps_for(display, window) - klass = XlibWrapper.fetch_window_class(display, window) - remaps_by_window[AnyWindow] + class_specific_remaps(klass) - end - - private - - def class_specific_remaps(klass) - @remaps_by_window.select do |window, _| - if !window.class_only.empty? - window.class_only.include?(klass) - elsif !window.class_not.empty? - !window.class_not.include?(klass) - else - false - end - end.map { |_, remaps| remaps }.flatten - end - end -end diff --git a/mrblib/xremap/config_dsl.rb b/mrblib/xremap/config_dsl.rb deleted file mode 100644 index b9dd633..0000000 --- a/mrblib/xremap/config_dsl.rb +++ /dev/null @@ -1,65 +0,0 @@ -module Xremap - class ConfigDSL - # @param [Xremap::Config] config - def initialize(config, win = Config::AnyWindow) - @config = config - @window = win - end - - def remap(from_str, options = {}) - # Array() doesn't work for Config::Execute somehow. - to_strs = options.fetch(:to) - to_strs = [to_strs] unless to_strs.is_a?(Array) - - @config.remaps_by_window[@window] << Config::Remap.new( - compile_exp(from_str), - to_strs.map { |str| compile_exp(str) } - ) - end - - def window(options = {}, &block) - win = Config::Window.new(options[:class_only], options[:class_not]) - ConfigDSL.new(@config, win).instance_exec(&block) - end - - def execute(str) - Config::Execute.new(str, :execute) - end - - def press(str) - key = compile_exp(str) - key.action = :press - key - end - - def release(str) - key = compile_exp(str) - key.action = :release - key - end - - def define(name, &block) - ConfigDSL.define_method(name, &block) - end - - def include_config(filename) - path = File.expand_path(filename, @config.config_dir) - path << '.rb' unless path.start_with?('.rb') - raise "config file not found!: #{path.inspect}" unless File.exist?(path) - instance_eval(File.read(path)) - end - - private - - def compile_exp(exp) - case exp - when Config::Key, Config::Execute - exp - when String - KeyExpression.compile(exp) - else - raise "unexpected expression: #{exp.inspect}" - end - end - end -end diff --git a/mrblib/xremap/event_handler.rb b/mrblib/xremap/event_handler.rb deleted file mode 100644 index 0d46090..0000000 --- a/mrblib/xremap/event_handler.rb +++ /dev/null @@ -1,39 +0,0 @@ -module Xremap - class EventHandler - # @param [Xremap::Config] config - # @param [Xremap::Display] display - def initialize(config, display) - @active_window = ActiveWindow.new(display) - @grab_manager = GrabManager.new(config, display) - @key_remap_compiler = KeyRemapCompiler.new(config, display) - remap_keys - end - - def handle_key_press(keycode, state) - handler = @key_press_handlers[keycode][state] - if handler - handler.call - else - $stderr.puts "Handler not found!: #{[keycode, state, @key_press_handlers].inspect}" - end - end - - def handle_property_notify - if @active_window.changed? - remap_keys - end - end - - def handle_mapping_notify - remap_keys - end - - private - - def remap_keys - window = @active_window.current_window - @key_press_handlers = @key_remap_compiler.compile_for(window) - @grab_manager.grab_keys_for(window) - end - end -end diff --git a/mrblib/xremap/grab_manager.rb b/mrblib/xremap/grab_manager.rb deleted file mode 100644 index 87c6ad5..0000000 --- a/mrblib/xremap/grab_manager.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Xremap - class GrabManager - # @param [Xremap::Config] config - # @param [Xremap::Display] display - def initialize(config, display) - @config = config - @display = display - end - - def grab_keys_for(window) - XlibWrapper.ungrab_keys(@display) - - # guard segmentation fault - return if window == 0 - - @config.remaps_for(@display, window).each do |remap| - from = remap.from_key - XlibWrapper.grab_key(@display, from.keysym, from.modifier) - end - - # TODO: remove this log - puts "remapped for class: #{XlibWrapper.fetch_window_class(@display, window).inspect}" - end - end -end diff --git a/mrblib/xremap/key_expression.rb b/mrblib/xremap/key_expression.rb deleted file mode 100644 index a322ef0..0000000 --- a/mrblib/xremap/key_expression.rb +++ /dev/null @@ -1,49 +0,0 @@ -module Xremap - module KeyExpression - class << self - # @param [String] exp - # @return [Xremap::Config::Key] key - def compile(exp) - keyexp, modifiers = split_into_key_and_mods(exp) - Config::Key.new(to_keysym(keyexp), modifier_mask(modifiers)) - end - - private - - def split_into_key_and_mods(exp) - modifiers = [] - while exp.match(/\A(?(C|Ctrl|M|Alt|Shift|Super|Win))-/) - modifier = Regexp.last_match[:modifier] - modifiers << modifier - exp = exp.sub(/\A#{modifier}-/, '') - end - [exp, modifiers] - end - - def modifier_mask(modifiers) - mask = X11::NoModifier - modifiers.each do |modifier| - case modifier - when 'C', 'Ctrl' - mask |= X11::ControlMask - when 'M', 'Alt' - mask |= X11::Mod1Mask - when 'Super', 'Win' - mask |= X11::Mod4Mask - when 'Shift' - mask |= X11::ShiftMask - end - end - mask - end - - def to_keysym(keyexp) - if keyexp.start_with?('XF86XK_') - X11.const_get(keyexp) - else - X11.const_get("XK_#{keyexp}") - end - end - end - end -end diff --git a/mrblib/xremap/key_remap_compiler.rb b/mrblib/xremap/key_remap_compiler.rb deleted file mode 100644 index 0ebd374..0000000 --- a/mrblib/xremap/key_remap_compiler.rb +++ /dev/null @@ -1,55 +0,0 @@ -module Xremap - class KeyRemapCompiler - def initialize(config, display) - @config = config - @display = display - puts "Config loaded: #{@config.inspect}" - end - - # @return [Hash] : keycode(Fixnum) -> state(Fixnum) -> handler(Proc) - def compile_for(window) - result = Hash.new { |h, k| h[k] = {} } - - # guard segmentation fault - return result if window == 0 - - set_handlers_for(result, window) - result - end - - private - - def set_handlers_for(result, window) - @config.remaps_for(@display, window).each do |remap| - from = remap.from_key - tos = remap.to_keys - - actions = remap.to_keys.map do |to| - case to.action - when :input - Proc.new { XlibWrapper.input_key(@display, to.keysym, to.modifier) } - when :press - Proc.new { XlibWrapper.press_key(@display, to.keysym, to.modifier) } - when :release - Proc.new { XlibWrapper.release_key(@display, to.keysym, to.modifier) } - when :execute - Proc.new { system("nohup /bin/sh -c #{to.command.shellescape} >/dev/null 2>&1 &") } - else - raise "unexpected action: #{to.action.inspect}" - end - end - - result[to_keycode(from.keysym)][from.modifier] = - if actions.length == 1 - actions.first - else - Proc.new { actions.each { |action| action.call } } - end - end - end - - def to_keycode(keysym) - XlibWrapper.keysym_to_keycode(@display, keysym) - end - end -end diff --git a/src/display.c b/src/display.c deleted file mode 100644 index 615936a..0000000 --- a/src/display.c +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include "mruby.h" -#include "mruby/data.h" - -struct mrb_display { - Display *display; -}; - -static void -mrb_display_free(mrb_state *mrb, void *ptr) -{ - struct mrb_display *display = (struct mrb_display *)ptr; - if (display != NULL) { - mrb_free(mrb, display); - } -} - -struct mrb_data_type mrb_display_type = { "Display", mrb_display_free }; - -mrb_value -mrb_wrap_x_display(mrb_state *mrb, Display *display) -{ - struct RClass *mXremap = mrb_module_get(mrb, "Xremap"); - struct RClass *cDisplay = mrb_class_get_under(mrb, mXremap, "Display"); - - struct mrb_display *display_ptr = (struct mrb_display *)mrb_malloc(mrb, sizeof(struct mrb_display)); - display_ptr->display = display; - mrb_value display_obj = mrb_obj_value(mrb_data_object_alloc(mrb, cDisplay, NULL, &mrb_display_type)); - DATA_TYPE(display_obj) = &mrb_display_type; - DATA_PTR(display_obj) = display_ptr; - return display_obj; -} - -Display* -extract_x_display(mrb_state *mrb, mrb_value display_obj) -{ - struct mrb_display *display_ptr = (struct mrb_display *)mrb_get_datatype(mrb, display_obj, &mrb_display_type); - return display_ptr->display; -} - -void -mrb_xremap_display_init(mrb_state *mrb, struct RClass *mXremap) -{ - mrb_define_class_under(mrb, mXremap, "Display", mrb->object_class); -} diff --git a/src/x11_constants.c b/src/x11_constants.c deleted file mode 100644 index bf68c89..0000000 --- a/src/x11_constants.c +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include -#include -#include "mruby.h" - -void -mrb_xremap_x11_constants_init(mrb_state *mrb, struct RClass *mXremap) -{ - struct RClass *mX11 = mrb_define_module_under(mrb, mXremap, "X11"); -# define define_x11_const(name) mrb_define_const(mrb, mX11, #name, mrb_fixnum_value(name)) - - // original constant. - mrb_define_const(mrb, mX11, "NoModifier", mrb_fixnum_value(0)); -#include "x11_constants_keysymdef.inc" -#include "x11_constants_X.inc" -#include "x11_constants_XF86keysym.inc" -} diff --git a/src/xlib_wrapper.c b/src/xlib_wrapper.c deleted file mode 100644 index eec5fe3..0000000 --- a/src/xlib_wrapper.c +++ /dev/null @@ -1,206 +0,0 @@ -#include -#include -#include -#include -#include "mruby.h" - -extern Display* extract_x_display(mrb_state *mrb, mrb_value display_obj); - -mrb_value -mrb_xw_fetch_window_class(mrb_state *mrb, mrb_value self) -{ - mrb_value display_obj; - mrb_int window; - mrb_get_args(mrb, "oi", &display_obj, &window); - - Display *display = extract_x_display(mrb, display_obj); - Atom net_wm_name = XInternAtom(display, "WM_CLASS", True); - - XTextProperty prop; - - // XGetTextProperty can return 0 when xmonad's workspace is selected, while it usually returns 1. - // This prevents SEGV in such a situation. - if (XGetTextProperty(display, window, &prop, net_wm_name) == 0) { - return display_obj; - } - - while (1) { - if (XGetTextProperty(display, window, &prop, net_wm_name)) { break; } - - unsigned int nchildren; - Window root, parent, *children; - - if (!XQueryTree(display, window, &root, &parent, &children, &nchildren)) { break; } - if (children) { XFree(children); } - window = parent; - } - - mrb_value ret; - if (prop.nitems > 0 && prop.value) { - if (prop.encoding == XA_STRING) { - ret = mrb_str_new_cstr(mrb, (char *)prop.value); - } else { - char **l = NULL; - int count; - XmbTextPropertyToTextList(display, &prop, &l, &count); - if (count > 0 && *l) { - ret = mrb_str_new_cstr(mrb, *l); - } else { - ret = mrb_str_new_cstr(mrb, ""); - } - XFreeStringList(l); - } - } - return ret; -} - -Window -get_focused_window(Display *display) -{ - Window window; - int focus_state; - - XGetInputFocus(display, &window, &focus_state); - return window; -} - -XKeyEvent -create_key_event(Display *display, Window window, KeySym keysym, unsigned int modifiers, int type) -{ - return (XKeyEvent){ - .display = display, - .window = window, - .root = XDefaultRootWindow(display), - .subwindow = None, - .time = CurrentTime, - .x = 1, - .y = 1, - .x_root = 1, - .y_root = 1, - .same_screen = True, - .keycode = XKeysymToKeycode(display, keysym), - .state = modifiers, - .type = type, - }; -} - -void -send_press_event(Display *display, Window window, KeySym keysym, unsigned int modifiers) -{ - XKeyEvent event = create_key_event(display, window, keysym, modifiers, KeyPress); - XSendEvent(display, window, True, KeyPressMask, (XEvent *)&event); -} - -void -send_release_event(Display *display, Window window, KeySym keysym, unsigned int modifiers) -{ - XKeyEvent event = create_key_event(display, window, keysym, modifiers, KeyRelease); - XSendEvent(display, window, True, KeyReleaseMask, (XEvent *)&event); -} - -mrb_value -mrb_xw_press_key(mrb_state *mrb, mrb_value self) -{ - mrb_value display_obj; - mrb_int keysym, modifiers; - mrb_get_args(mrb, "oii", &display_obj, &keysym, &modifiers); - - Display *display = extract_x_display(mrb, display_obj); - Window window = get_focused_window(display); - - send_press_event(display, window, keysym, modifiers); - - return mrb_nil_value(); -} - -mrb_value -mrb_xw_release_key(mrb_state *mrb, mrb_value self) -{ - mrb_value display_obj; - mrb_int keysym, modifiers; - mrb_get_args(mrb, "oii", &display_obj, &keysym, &modifiers); - - Display *display = extract_x_display(mrb, display_obj); - Window window = get_focused_window(display); - - send_release_event(display, window, keysym, modifiers); - - return mrb_nil_value(); -} - -mrb_value -mrb_xw_input_key(mrb_state *mrb, mrb_value self) -{ - mrb_value display_obj; - mrb_int keysym, modifiers; - mrb_get_args(mrb, "oii", &display_obj, &keysym, &modifiers); - - Display *display = extract_x_display(mrb, display_obj); - Window window = get_focused_window(display); - - send_press_event(display, window, keysym, modifiers); - send_release_event(display, window, keysym, modifiers); - - return mrb_nil_value(); -} - -mrb_value -mrb_xw_keysym_to_keycode(mrb_state *mrb, mrb_value self) -{ - mrb_value display_obj; - mrb_int keysym; - mrb_get_args(mrb, "oi", &display_obj, &keysym); - - Display *display = extract_x_display(mrb, display_obj); - return mrb_fixnum_value(XKeysymToKeycode(display, keysym)); -} - -mrb_value -mrb_xw_fetch_active_window(mrb_state *mrb, mrb_value self) -{ - mrb_value display_obj; - mrb_int keycode, state; - mrb_get_args(mrb, "o", &display_obj, &keycode, &state); - - Display *display = extract_x_display(mrb, display_obj); - return mrb_fixnum_value(get_focused_window(display)); -} - -mrb_value -mrb_xw_grab_key(mrb_state *mrb, mrb_value self) -{ - mrb_value display_obj; - mrb_int keycode, state; - mrb_get_args(mrb, "oii", &display_obj, &keycode, &state); - - Display *display = extract_x_display(mrb, display_obj); - XGrabKey(display, XKeysymToKeycode(display, keycode), state, XDefaultRootWindow(display), True, GrabModeAsync, GrabModeAsync); - - return mrb_nil_value(); -} - -mrb_value -mrb_xw_ungrab_keys(mrb_state *mrb, mrb_value self) -{ - mrb_value display_obj; - mrb_get_args(mrb, "o", &display_obj); - - Display *display = extract_x_display(mrb, display_obj); - XUngrabKey(display, AnyKey, AnyModifier, XDefaultRootWindow(display)); - - return mrb_nil_value(); -} - -void -mrb_xremap_xlib_wrapper_init(mrb_state *mrb, struct RClass *mXremap) -{ - struct RClass *cXlibWrapper = mrb_define_class_under(mrb, mXremap, "XlibWrapper", mrb->object_class); - mrb_define_class_method(mrb, cXlibWrapper, "input_key", mrb_xw_input_key, MRB_ARGS_REQ(3)); - mrb_define_class_method(mrb, cXlibWrapper, "press_key", mrb_xw_press_key, MRB_ARGS_REQ(3)); - mrb_define_class_method(mrb, cXlibWrapper, "release_key", mrb_xw_release_key, MRB_ARGS_REQ(3)); - mrb_define_class_method(mrb, cXlibWrapper, "keysym_to_keycode", mrb_xw_keysym_to_keycode, MRB_ARGS_REQ(2)); - mrb_define_class_method(mrb, cXlibWrapper, "fetch_active_window", mrb_xw_fetch_active_window, MRB_ARGS_REQ(1)); - mrb_define_class_method(mrb, cXlibWrapper, "fetch_window_class", mrb_xw_fetch_window_class, MRB_ARGS_REQ(2)); - mrb_define_class_method(mrb, cXlibWrapper, "grab_key", mrb_xw_grab_key, MRB_ARGS_REQ(3)); - mrb_define_class_method(mrb, cXlibWrapper, "ungrab_keys", mrb_xw_ungrab_keys, MRB_ARGS_REQ(1)); -} diff --git a/src/xremap_gem.c b/src/xremap_gem.c deleted file mode 100644 index 9edc46d..0000000 --- a/src/xremap_gem.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "mruby.h" - -extern void mrb_xremap_xlib_wrapper_init(mrb_state *mrb, struct RClass *mXremap); -extern void mrb_xremap_display_init(mrb_state *mrb, struct RClass *mXremap); -extern void mrb_xremap_x11_constants_init(mrb_state *mrb, struct RClass *mXremap); - -void -mrb_xremap_gem_init(mrb_state *mrb) -{ - struct RClass *mXremap = mrb_define_module(mrb, "Xremap"); - - mrb_xremap_xlib_wrapper_init(mrb, mXremap); - mrb_xremap_display_init(mrb, mXremap); - mrb_xremap_x11_constants_init(mrb, mXremap); - mrb_gc_arena_restore(mrb, 0); -} - -void -mrb_xremap_gem_final(mrb_state *mrb) -{ -} diff --git a/tools/xremap/config.c b/tools/xremap/config.c deleted file mode 100644 index 319fece..0000000 --- a/tools/xremap/config.c +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include -#include -#include - -mrb_value -load_config(mrb_state *mrb, char *filename) -{ - struct RClass *mXremap = mrb_module_get(mrb, "Xremap"); - struct RClass *cConfig = mrb_class_get_under(mrb, mXremap, "Config"); - - mrb_value config = mrb_funcall(mrb, mrb_obj_value(cConfig), "load", 1, mrb_str_new_cstr(mrb, filename)); - if (mrb->exc) { - mrb_print_error(mrb); - } - return config; -} diff --git a/tools/xremap/event_handler.c b/tools/xremap/event_handler.c deleted file mode 100644 index dc6fc46..0000000 --- a/tools/xremap/event_handler.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include - -extern mrb_value mrb_wrap_x_display(mrb_state *mrb, Display *display); - -mrb_value -new_event_handler(mrb_state *mrb, mrb_value config, Display *display) -{ - struct RClass *mXremap = mrb_module_get(mrb, "Xremap"); - struct RClass *cEventHandler = mrb_class_get_under(mrb, mXremap, "EventHandler"); - mrb_value display_obj = mrb_wrap_x_display(mrb, display); - return mrb_funcall(mrb, mrb_obj_value(cEventHandler), "new", 2, config, display_obj); -} - -void -handle_key_press(mrb_state *mrb, mrb_value event_handler, unsigned int keycode, unsigned int state) -{ - mrb_funcall(mrb, event_handler, "handle_key_press", 2, - mrb_fixnum_value(keycode), mrb_fixnum_value(state)); - if (mrb->exc) { - mrb_print_error(mrb); - } -} - -void -handle_property_notify(mrb_state *mrb, mrb_value event_handler) -{ - mrb_funcall(mrb, event_handler, "handle_property_notify", 0); - if (mrb->exc) { - mrb_print_error(mrb); - } -} - -void -handle_mapping_notify(mrb_state *mrb, mrb_value event_handler) -{ - mrb_funcall(mrb, event_handler, "handle_mapping_notify", 0); - if (mrb->exc) { - mrb_print_error(mrb); - } -} diff --git a/tools/xremap/main.c b/tools/xremap/main.c deleted file mode 100644 index b433ef3..0000000 --- a/tools/xremap/main.c +++ /dev/null @@ -1,96 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "xremap.h" - -void -print_client_message_event(XClientMessageEvent *event) -{ - fprintf(stderr, - "received ClientMesssage(message_type=%" PRIu32 " format=%d data=%#lx, %#lx, %#lx, %#lx, %#lx)", - (uint32_t)event->message_type, - event->format, - (unsigned long)event->data.l[0], - (unsigned long)event->data.l[1], - (unsigned long)event->data.l[2], - (unsigned long)event->data.l[3], - (unsigned long)event->data.l[4]); - -} - -int -error_handler(Display *display, XErrorEvent *event) -{ - char buffer[1024]; - - if (!XGetErrorText(display, event->error_code, buffer, sizeof(buffer))) { - buffer[0] = '\0'; - } - - fprintf(stderr, - "error detected! XErrorEvent(serial=%ld error_code=%d request_code=%d minor_code=%d text=%s)\n", - event->serial, - event->error_code, - event->request_code, - event->minor_code, - buffer); - return 0; -} - -void -event_loop(Display *display, mrb_state *mrb, mrb_value event_handler) -{ - XEvent event; - while (1) { - XNextEvent(display, &event); - switch (event.type) { - case KeyPress: - handle_key_press(mrb, event_handler, event.xkey.keycode, event.xkey.state); - break; - case KeyRelease: - // ignore. Is it necessary to handle this? - break; - case PropertyNotify: - handle_property_notify(mrb, event_handler); - break; - case MappingNotify: - handle_mapping_notify(mrb, event_handler); - break; - case ClientMessage: - print_client_message_event((XClientMessageEvent*)&event); - default: - fprintf(stderr, "unexpected event detected! (%d)\n", event.type); - break; - } - } -} - -int -main(int argc, char **argv) -{ - if (argc != 2) { - fprintf(stderr, "Usage: xremap \n"); - return 1; - } - - mrb_state *mrb = mrb_open(); - mrb_value config = load_config(mrb, argv[1]); - - Display *display = XOpenDisplay(NULL); - if (!display) { - fprintf(stderr, "Failed to open connection with X server!\n"); - return 1; - } - - XSetErrorHandler(error_handler); - XSelectInput(display, XDefaultRootWindow(display), KeyPressMask | PropertyChangeMask); - - mrb_value event_handler = new_event_handler(mrb, config, display); - event_loop(display, mrb, event_handler); - - XCloseDisplay(display); - return 0; -} diff --git a/tools/xremap/xremap.h b/tools/xremap/xremap.h deleted file mode 100644 index 5e774ed..0000000 --- a/tools/xremap/xremap.h +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include - -// config.c -mrb_value load_config(mrb_state *mrb, char *filename); - -// event_handler.c -mrb_value new_event_handler(mrb_state *mrb, mrb_value config, Display *display); -void handle_key_press(mrb_state *mrb, mrb_value event_handler, unsigned int state, unsigned int keycode); -void handle_property_notify(mrb_state *mrb, mrb_value event_handler); -void handle_mapping_notify(mrb_state *mrb, mrb_value event_handler);