rlirc 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/INSTALL +14 -0
- data/LICENSE +26 -0
- data/README +29 -0
- data/Rakefile +106 -0
- data/bin/rlirc +25 -0
- data/data/examples/rlirc.rb +123 -0
- data/ext/xevent/MANIFEST +2 -0
- data/ext/xevent/extconf.rb +9 -0
- data/ext/xevent/xevent.c +574 -0
- data/lib/rlirc.rb +59 -0
- data/lib/rlirc/aumix.rb +29 -0
- data/lib/rlirc/config.rb +20 -0
- data/lib/rlirc/event.rb +48 -0
- data/lib/rlirc/osd.rb +19 -0
- data/lib/rlirc/xevent.rb +15 -0
- data/setup.rb +1331 -0
- metadata +66 -0
data/INSTALL
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
De-Compress archive and enter its top directory.
|
2
|
+
Then type:
|
3
|
+
|
4
|
+
$ ruby setup.rb config
|
5
|
+
$ ruby setup.rb setup
|
6
|
+
($ su)
|
7
|
+
# ruby setup.rb install
|
8
|
+
|
9
|
+
You can also install files into your favorite directory
|
10
|
+
by supplying setup.rb some options. Try "ruby setup.rb --help".
|
11
|
+
|
12
|
+
There is also a Rakefile for generating packages, rdoc, ...
|
13
|
+
just type rake in the top directory to see a list of options
|
14
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2004, Roeland Moors
|
2
|
+
|
3
|
+
All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
6
|
+
modification, are permitted provided that the following conditions
|
7
|
+
are met:
|
8
|
+
1. Redistributions of source code must retain the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
12
|
+
documentation and/or other materials provided with the distribution.
|
13
|
+
3. The name of the author may not be used to endorse or promote products
|
14
|
+
derived from this software without specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
17
|
+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
18
|
+
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
19
|
+
THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
20
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
21
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
22
|
+
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
23
|
+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
24
|
+
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
25
|
+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
|
26
|
+
|
data/README
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
= rlirc README
|
2
|
+
|
3
|
+
rlirc is a replacement for irexec (lirc)
|
4
|
+
|
5
|
+
== Requirements
|
6
|
+
|
7
|
+
* ruby 1.8
|
8
|
+
* lirc
|
9
|
+
|
10
|
+
== Install
|
11
|
+
|
12
|
+
:include: INSTALL
|
13
|
+
|
14
|
+
== Documentation
|
15
|
+
|
16
|
+
http://rlirc.rubyforge.org
|
17
|
+
|
18
|
+
== Support
|
19
|
+
|
20
|
+
The homepage is at http://rlirc.rubyforge.org.
|
21
|
+
Go to the project page for more information.
|
22
|
+
|
23
|
+
Feel free to submit commits or feature requests.
|
24
|
+
|
25
|
+
== License
|
26
|
+
|
27
|
+
Gnucap-Ruby is available under a BSD license
|
28
|
+
|
29
|
+
:include: LICENSE
|
data/Rakefile
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Rakefile for rlirc
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require 'rake/clean'
|
7
|
+
|
8
|
+
#############
|
9
|
+
# rake help #
|
10
|
+
#############
|
11
|
+
|
12
|
+
desc "Default Task (show this help)"
|
13
|
+
task :default => :help
|
14
|
+
|
15
|
+
desc "Show this help"
|
16
|
+
task :help do
|
17
|
+
sh "rake --task"
|
18
|
+
end
|
19
|
+
|
20
|
+
################
|
21
|
+
# rake version #
|
22
|
+
################
|
23
|
+
|
24
|
+
version = `grep 'REVISION =' #{Dir.pwd}/bin/rlirc`
|
25
|
+
PKG_VERSION = version.split('=')[1].chomp.delete(' ').delete("'")
|
26
|
+
PKG_NAME = 'rlirc'
|
27
|
+
|
28
|
+
desc "Show version of rlirc"
|
29
|
+
task :version do
|
30
|
+
puts "Version: #{PKG_VERSION}"
|
31
|
+
end
|
32
|
+
|
33
|
+
################
|
34
|
+
# rake install #
|
35
|
+
################
|
36
|
+
|
37
|
+
desc "Info about installing rlirc"
|
38
|
+
task :install do
|
39
|
+
install_info = <<-EOS
|
40
|
+
Typical installation procedure is:
|
41
|
+
$ ruby setup.rb config
|
42
|
+
$ ruby setup.rb setup
|
43
|
+
# ruby setup.rb install (may require root privilege)
|
44
|
+
Try 'ruby setup.rb --help' for detailed usage.
|
45
|
+
EOS
|
46
|
+
|
47
|
+
puts install_info
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
#############
|
52
|
+
# rake rdoc #
|
53
|
+
#############
|
54
|
+
|
55
|
+
Rake::RDocTask.new do |rd|
|
56
|
+
rd.rdoc_dir = 'rdoc'
|
57
|
+
rd.main = 'README'
|
58
|
+
rd.rdoc_files.include('README', 'LICENSE', 'lib/**/*.rb')
|
59
|
+
rd.options << '-S'
|
60
|
+
end
|
61
|
+
|
62
|
+
#################
|
63
|
+
# rake packages #
|
64
|
+
#################
|
65
|
+
|
66
|
+
PKG_FILES = FileList[
|
67
|
+
'setup.rb',
|
68
|
+
'[A-Z]*',
|
69
|
+
'lib/**/*.rb',
|
70
|
+
'data/**/*.rb',
|
71
|
+
'bin/**/*',
|
72
|
+
'ext/**/*'
|
73
|
+
]
|
74
|
+
|
75
|
+
gemspec = Gem::Specification.new do |s|
|
76
|
+
s.author = "Roeland Moors"
|
77
|
+
s.autorequire = 'rlirc'
|
78
|
+
s.bindir = 'bin'
|
79
|
+
s.default_executable = 'rlirc'
|
80
|
+
s.add_dependency('libxosd2-ruby', '>= 0.4')
|
81
|
+
s.description = "a replacement for irexec and irxevent from lirc"
|
82
|
+
s.email = "roelandmoors@telenet.be"
|
83
|
+
s.executables = ['rlirc']
|
84
|
+
s.extensions << "ext/xevent/extconf.rb"
|
85
|
+
s.extra_rdoc_files = ['README']
|
86
|
+
s.files = PKG_FILES.to_a
|
87
|
+
s.has_rdoc = true
|
88
|
+
s.homepage = "http://rlirc.rubyforge.org"
|
89
|
+
s.name = PKG_NAME
|
90
|
+
#s.platform
|
91
|
+
#s.rdoc_options
|
92
|
+
s.require_path = 'lib'
|
93
|
+
s.required_ruby_version = '>= 1.8.1'
|
94
|
+
s.requirements << 'X dev libs'
|
95
|
+
s.rubyforge_project = "rlirc"
|
96
|
+
s.summary = "a replacement for irexec and irxevent from lirc"
|
97
|
+
#s.test_files
|
98
|
+
s.version = PKG_VERSION
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
Rake::GemPackageTask.new(gemspec) do |pkg|
|
103
|
+
pkg.need_zip = false
|
104
|
+
pkg.need_tar = true
|
105
|
+
end
|
106
|
+
|
data/bin/rlirc
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rlirc'
|
4
|
+
|
5
|
+
REVISION = '0.3.1'
|
6
|
+
|
7
|
+
begin
|
8
|
+
Rlirc.new(ARGV)
|
9
|
+
rescue => e
|
10
|
+
$stderr.puts <<EOS
|
11
|
+
-----------------------
|
12
|
+
Rlirc just crashed
|
13
|
+
-----------------------
|
14
|
+
Timestamp: #{Time.now}
|
15
|
+
Message: #{e.message}
|
16
|
+
Backtrace:
|
17
|
+
#{e.backtrace.join("\n")}
|
18
|
+
Release: #{REVISION}
|
19
|
+
Uname -a: #{`uname -a`.chomp}
|
20
|
+
EOS
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'rlirc'
|
2
|
+
|
3
|
+
# only needed if you need OSD
|
4
|
+
require 'rlirc/osd'
|
5
|
+
|
6
|
+
# aumix functions
|
7
|
+
require 'rlirc/aumix'
|
8
|
+
|
9
|
+
# send x events
|
10
|
+
require 'rlirc/xevent'
|
11
|
+
|
12
|
+
# lirc device
|
13
|
+
$device = '/dev/lircd'
|
14
|
+
|
15
|
+
# show debug information
|
16
|
+
$debug = false
|
17
|
+
|
18
|
+
# prevents unwanted repeated presses
|
19
|
+
# (under construction)
|
20
|
+
$delay = 0
|
21
|
+
|
22
|
+
# osd settings, look at these webpages for other options:
|
23
|
+
# http://rubyforge.org/projects/libxosd-ruby/
|
24
|
+
# http://ldots.org/xosd-guide/using_libxosd.html
|
25
|
+
$osd.timeout = 2
|
26
|
+
|
27
|
+
|
28
|
+
# This is the default class
|
29
|
+
class ModeMain < Mode
|
30
|
+
def initialize
|
31
|
+
end
|
32
|
+
|
33
|
+
def button_tv
|
34
|
+
change_mode(ModeTV)
|
35
|
+
end
|
36
|
+
|
37
|
+
def button_radio
|
38
|
+
change_mode(ModeRadio)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
# a button defined here is always executed
|
45
|
+
class ModeGlobal < Mode
|
46
|
+
def button_volup
|
47
|
+
`aumix -v+5`
|
48
|
+
end
|
49
|
+
|
50
|
+
def button_volup_repeat(count)
|
51
|
+
button_volup
|
52
|
+
end
|
53
|
+
|
54
|
+
def button_voldown
|
55
|
+
`aumix -v-5`
|
56
|
+
end
|
57
|
+
|
58
|
+
def button_voldown_repeat(count)
|
59
|
+
button_voldown
|
60
|
+
end
|
61
|
+
|
62
|
+
def button_mute
|
63
|
+
aumix_mute_toggle
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class ModeRadio < Mode
|
68
|
+
def initialize
|
69
|
+
button_1
|
70
|
+
end
|
71
|
+
|
72
|
+
def button_radio
|
73
|
+
`radio -qm`
|
74
|
+
end_mode # == change_mode(ModeMain)
|
75
|
+
end
|
76
|
+
|
77
|
+
def button_1
|
78
|
+
osd "StuBru"
|
79
|
+
`radio -qf 96.6`
|
80
|
+
end
|
81
|
+
|
82
|
+
def button_2
|
83
|
+
osd "Donna"
|
84
|
+
`radio -qf 101.7`
|
85
|
+
end
|
86
|
+
|
87
|
+
def button_tv
|
88
|
+
`radio -qm`
|
89
|
+
change_mode(ModeTV)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class ModeTV < Mode
|
94
|
+
def initialize
|
95
|
+
system("tvtime &")
|
96
|
+
end
|
97
|
+
|
98
|
+
def button_tv
|
99
|
+
`tvtime-command QUIT`
|
100
|
+
end_mode # == change_mode(ModeMain)
|
101
|
+
end
|
102
|
+
|
103
|
+
def button_radio
|
104
|
+
`tvtime-command QUIT`
|
105
|
+
change_mode(ModeRadio)
|
106
|
+
end
|
107
|
+
|
108
|
+
def button_chup
|
109
|
+
`tvtime-command UP`
|
110
|
+
end
|
111
|
+
|
112
|
+
def button_chdown
|
113
|
+
`tvtime-command DOWN`
|
114
|
+
end
|
115
|
+
|
116
|
+
def number(nr)
|
117
|
+
`tvtime-command CHANNEL_#{nr}`
|
118
|
+
end
|
119
|
+
|
120
|
+
def button_full_screen
|
121
|
+
xevent('Key f tvtime')
|
122
|
+
end
|
123
|
+
end
|
data/ext/xevent/MANIFEST
ADDED
data/ext/xevent/xevent.c
ADDED
@@ -0,0 +1,574 @@
|
|
1
|
+
/*
|
2
|
+
* The most of this is code is from irxevent.c in the lirc package
|
3
|
+
* I used the code from lirc version 0.7.0pre7.
|
4
|
+
* This means this code is under the same license (GPL)
|
5
|
+
*
|
6
|
+
* Here is the copyright stuff from the original file:
|
7
|
+
* ---
|
8
|
+
* Heinrich Langos <heinrich@null.net>
|
9
|
+
* small modifications by Christoph Bartelmus <lirc@bartelmus.de>
|
10
|
+
*
|
11
|
+
* irxevent is based on irexec (Copyright (C) 1998 Trent Piepho)
|
12
|
+
* and irx.c (no copyright notice found)
|
13
|
+
* ---
|
14
|
+
*
|
15
|
+
* Here is the changelog
|
16
|
+
*
|
17
|
+
* 0.1: (Roeland Moors)
|
18
|
+
* removed the lirc config stuff
|
19
|
+
* added ruby stuff
|
20
|
+
*/
|
21
|
+
|
22
|
+
#ifdef HAVE_CONFIG_H
|
23
|
+
# include <config.h>
|
24
|
+
#endif
|
25
|
+
|
26
|
+
#include <errno.h>
|
27
|
+
#include <unistd.h>
|
28
|
+
#include <getopt.h>
|
29
|
+
#include <stdarg.h>
|
30
|
+
#include <stdio.h>
|
31
|
+
#include <stdlib.h>
|
32
|
+
#include <string.h>
|
33
|
+
#include <sys/socket.h>
|
34
|
+
#include <sys/un.h>
|
35
|
+
#include <sys/stat.h>
|
36
|
+
#include <sys/types.h>
|
37
|
+
|
38
|
+
#include <X11/Xlib.h>
|
39
|
+
#include <X11/Xutil.h>
|
40
|
+
#include <sys/time.h>
|
41
|
+
#include <unistd.h>
|
42
|
+
|
43
|
+
#include <ruby.h>
|
44
|
+
|
45
|
+
#define DEBUG
|
46
|
+
#ifdef DEBUG
|
47
|
+
void debugprintf(char *format_str, ...)
|
48
|
+
{
|
49
|
+
va_list ap;
|
50
|
+
va_start(ap,format_str);
|
51
|
+
vfprintf(stderr,format_str,ap);
|
52
|
+
va_end(ap);
|
53
|
+
}
|
54
|
+
#else
|
55
|
+
void debugprintf(char *format_str, ...)
|
56
|
+
{
|
57
|
+
}
|
58
|
+
#endif
|
59
|
+
|
60
|
+
|
61
|
+
struct keymodlist_t {
|
62
|
+
char *name;
|
63
|
+
Mask mask;
|
64
|
+
};
|
65
|
+
static struct keymodlist_t keymodlist[]=
|
66
|
+
{
|
67
|
+
{"SHIFT", ShiftMask},
|
68
|
+
{"CAPS", LockMask},
|
69
|
+
{"CTRL", ControlMask},
|
70
|
+
{"ALT", Mod1Mask},{"META", Mod1Mask},
|
71
|
+
{"NUMLOCK", Mod2Mask},
|
72
|
+
{"MOD3", Mod3Mask}, /* I don't have a clue what key maps to this. */
|
73
|
+
{"MOD4", Mod4Mask}, /* I don't have a clue what key maps to this. */
|
74
|
+
{"SCRLOCK", Mod5Mask},
|
75
|
+
{NULL,0},
|
76
|
+
};
|
77
|
+
|
78
|
+
const char *key_delimiter ="-";
|
79
|
+
const char *active_window_name ="CurrentWindow";
|
80
|
+
const char *root_window_name ="RootWindow";
|
81
|
+
|
82
|
+
|
83
|
+
char *progname;
|
84
|
+
Display *dpy;
|
85
|
+
Window root;
|
86
|
+
XEvent xev;
|
87
|
+
Window w,subw;
|
88
|
+
|
89
|
+
Time fake_timestamp()
|
90
|
+
/*seems that xfree86 computes the timestamps like this */
|
91
|
+
/*strange but it relies on the *1000-32bit-wrap-around */
|
92
|
+
/*if anybody knows exactly how to do it, please contact me */
|
93
|
+
{
|
94
|
+
int tint;
|
95
|
+
struct timeval tv;
|
96
|
+
struct timezone tz; /* is not used since ages */
|
97
|
+
gettimeofday(&tv,&tz);
|
98
|
+
tint=(int)tv.tv_sec*1000;
|
99
|
+
tint=tint/1000*1000;
|
100
|
+
tint=tint+tv.tv_usec/1000;
|
101
|
+
return (Time)tint;
|
102
|
+
}
|
103
|
+
|
104
|
+
Window find_window(Window top,char *name)
|
105
|
+
{
|
106
|
+
char *wname,*iname;
|
107
|
+
XClassHint xch;
|
108
|
+
Window *children,foo;
|
109
|
+
int revert_to_return;
|
110
|
+
unsigned int nc;
|
111
|
+
if (!strcmp(active_window_name,name)){
|
112
|
+
XGetInputFocus(dpy, &foo, &revert_to_return);
|
113
|
+
return(foo);
|
114
|
+
} else if (!strcmp(root_window_name,name)){
|
115
|
+
return(root);
|
116
|
+
}
|
117
|
+
/* First the base case */
|
118
|
+
if (XFetchName(dpy,top,&wname)){
|
119
|
+
if (!strncmp(wname,name,strlen(name))) {
|
120
|
+
XFree(wname);
|
121
|
+
debugprintf("found it by wname 0x%x \n",top);
|
122
|
+
return(top); /* found it! */
|
123
|
+
};
|
124
|
+
XFree(wname);
|
125
|
+
};
|
126
|
+
|
127
|
+
if(XGetIconName(dpy,top,&iname)){
|
128
|
+
if (!strncmp(iname,name,strlen(name))) {
|
129
|
+
XFree(iname);
|
130
|
+
debugprintf("found it by iname 0x%x \n",top);
|
131
|
+
return(top); /* found it! */
|
132
|
+
};
|
133
|
+
XFree(iname);
|
134
|
+
};
|
135
|
+
|
136
|
+
if(XGetClassHint(dpy,top,&xch)) {
|
137
|
+
if(!strcmp(xch.res_class,name)) {
|
138
|
+
XFree(xch.res_name); XFree(xch.res_class);
|
139
|
+
debugprintf("res_class '%s' res_name '%s' 0x%x \n", xch.res_class,xch.res_name,top);
|
140
|
+
return(top); /* found it! */
|
141
|
+
};
|
142
|
+
if(!strcmp(xch.res_name,name)) {
|
143
|
+
XFree(xch.res_name); XFree(xch.res_class);
|
144
|
+
debugprintf("res_class '%s' res_name '%s' 0x%x \n", xch.res_class,xch.res_name,top);
|
145
|
+
return(top); /* found it! */
|
146
|
+
};
|
147
|
+
XFree(xch.res_name); XFree(xch.res_class);
|
148
|
+
};
|
149
|
+
|
150
|
+
if(!XQueryTree(dpy,top,&foo,&foo,&children,&nc) || children==NULL) {
|
151
|
+
return(0); /* no more windows here */
|
152
|
+
};
|
153
|
+
|
154
|
+
/* check all the sub windows */
|
155
|
+
for(;nc>0;nc--) {
|
156
|
+
top = find_window(children[nc-1],name);
|
157
|
+
if(top) break; /* we found it somewhere */
|
158
|
+
};
|
159
|
+
if(children!=NULL) XFree(children);
|
160
|
+
return(top);
|
161
|
+
}
|
162
|
+
|
163
|
+
Window find_sub_sub_window(Window top,int *x, int *y)
|
164
|
+
{
|
165
|
+
Window base;
|
166
|
+
Window *children,foo,target=0;
|
167
|
+
unsigned int nc,
|
168
|
+
rel_x,rel_y,width,height,border,depth,
|
169
|
+
new_x=1,new_y=1,
|
170
|
+
targetsize=1000000;
|
171
|
+
|
172
|
+
base=top;
|
173
|
+
if (!base) {return base;};
|
174
|
+
if(!XQueryTree(dpy,base,&foo,&foo,&children,&nc) || children==NULL) {
|
175
|
+
return(base); /* no more windows here */
|
176
|
+
};
|
177
|
+
debugprintf("found subwindows %d\n",nc);
|
178
|
+
|
179
|
+
/* check if we hit a sub window and find the smallest one */
|
180
|
+
for(;nc>0;nc--) {
|
181
|
+
if(XGetGeometry(dpy, children[nc-1], &foo, &rel_x, &rel_y,
|
182
|
+
&width, &height, &border, &depth)){
|
183
|
+
if ((rel_x<=*x)&&(*x<=rel_x+width)&&(rel_y<=*y)&&(*y<=rel_y+height)){
|
184
|
+
debugprintf("found a subwindow 0x%x +%d +%d %d x %d \n",children[nc-1], rel_x,rel_y,width,height);
|
185
|
+
if ((width*height)<targetsize){
|
186
|
+
target=children[nc-1];
|
187
|
+
targetsize=width*height;
|
188
|
+
new_x=*x-rel_x;
|
189
|
+
new_y=*y-rel_y;
|
190
|
+
/*bull's eye ...*/
|
191
|
+
target=find_sub_sub_window(target,&new_x,&new_y);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
}
|
195
|
+
};
|
196
|
+
if(children!=NULL) XFree(children);
|
197
|
+
if (target){
|
198
|
+
*x=new_x;
|
199
|
+
*y=new_y;
|
200
|
+
return target;
|
201
|
+
}else
|
202
|
+
return base;
|
203
|
+
}
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
Window find_sub_window(Window top,char *name,int *x, int *y)
|
208
|
+
{
|
209
|
+
Window base;
|
210
|
+
Window *children,foo,target=0;
|
211
|
+
unsigned int nc,
|
212
|
+
rel_x,rel_y,width,height,border,depth,
|
213
|
+
new_x=1,new_y=1,
|
214
|
+
targetsize=1000000;
|
215
|
+
|
216
|
+
base=find_window(top, name);
|
217
|
+
if (!base) {return base;};
|
218
|
+
if(!XQueryTree(dpy,base,&foo,&foo,&children,&nc) || children==NULL) {
|
219
|
+
return(base); /* no more windows here */
|
220
|
+
};
|
221
|
+
debugprintf("found subwindows %d\n",nc);
|
222
|
+
|
223
|
+
/* check if we hit a sub window and find the smallest one */
|
224
|
+
for(;nc>0;nc--) {
|
225
|
+
if(XGetGeometry(dpy, children[nc-1], &foo, &rel_x, &rel_y,
|
226
|
+
&width, &height, &border, &depth)){
|
227
|
+
if ((rel_x<=*x)&&(*x<=rel_x+width)&&(rel_y<=*y)&&(*y<=rel_y+height)){
|
228
|
+
debugprintf("found a subwindow 0x%x +%d +%d %d x %d \n",children[nc-1], rel_x,rel_y,width,height);
|
229
|
+
if ((width*height)<targetsize){
|
230
|
+
target=children[nc-1];
|
231
|
+
targetsize=width*height;
|
232
|
+
new_x=*x-rel_x;
|
233
|
+
new_y=*y-rel_y;
|
234
|
+
/*bull's eye ...*/
|
235
|
+
target=find_sub_sub_window(target,&new_x,&new_y);
|
236
|
+
}
|
237
|
+
}
|
238
|
+
}
|
239
|
+
};
|
240
|
+
if(children!=NULL) XFree(children);
|
241
|
+
if (target){
|
242
|
+
*x=new_x;
|
243
|
+
*y=new_y;
|
244
|
+
return target;
|
245
|
+
}else
|
246
|
+
return base;
|
247
|
+
}
|
248
|
+
|
249
|
+
|
250
|
+
Window find_window_focused(Window top,char *name)
|
251
|
+
{
|
252
|
+
int tmp;
|
253
|
+
Window w, cur, *children, foo;
|
254
|
+
unsigned int n;
|
255
|
+
|
256
|
+
|
257
|
+
/* return the currently focused window if it is a direct match or a
|
258
|
+
subwindow of the named window */
|
259
|
+
|
260
|
+
if((w=find_window(top,name))) {
|
261
|
+
XGetInputFocus(dpy, &cur, &tmp);
|
262
|
+
debugprintf("current window: 0x%x named window: 0x%x\n",cur,w);
|
263
|
+
|
264
|
+
if( w == cur ) {
|
265
|
+
/* window matched */
|
266
|
+
return(cur);
|
267
|
+
}
|
268
|
+
else if(XQueryTree(dpy,w,&foo,&foo,&children,&n) && children!=NULL) {
|
269
|
+
/* check all the sub windows of named window */
|
270
|
+
for(;n>0;n--) {
|
271
|
+
if(children[n-1] == cur ) {
|
272
|
+
XFree(children);
|
273
|
+
return(cur);
|
274
|
+
}
|
275
|
+
}
|
276
|
+
XFree(children);
|
277
|
+
}
|
278
|
+
}
|
279
|
+
|
280
|
+
return(0);
|
281
|
+
}
|
282
|
+
|
283
|
+
void make_button(int button,int x,int y,XButtonEvent *xev)
|
284
|
+
{
|
285
|
+
xev->type = ButtonPress;
|
286
|
+
xev->display=dpy;
|
287
|
+
xev->root=root;
|
288
|
+
xev->subwindow=None;
|
289
|
+
xev->time=fake_timestamp();
|
290
|
+
xev->x=x;xev->y=y;
|
291
|
+
xev->x_root=1; xev->y_root=1;
|
292
|
+
xev->state=0;
|
293
|
+
xev->button=button;
|
294
|
+
xev->same_screen=True;
|
295
|
+
|
296
|
+
return;
|
297
|
+
}
|
298
|
+
|
299
|
+
void make_key(char *keyname,int x, int y,XKeyEvent *xev)
|
300
|
+
{
|
301
|
+
char *part, *part2;
|
302
|
+
struct keymodlist_t *kmlptr;
|
303
|
+
|
304
|
+
part2=malloc(128);
|
305
|
+
|
306
|
+
xev->type = KeyPress;
|
307
|
+
xev->display=dpy;
|
308
|
+
xev->root=root;
|
309
|
+
xev->subwindow = None;
|
310
|
+
xev->time=fake_timestamp();
|
311
|
+
xev->x=x; xev->y=y;
|
312
|
+
xev->x_root=1; xev->y_root=1;
|
313
|
+
xev->same_screen = True;
|
314
|
+
|
315
|
+
xev->state=0;
|
316
|
+
#ifdef HAVE_STRSEP
|
317
|
+
while ((part=strsep(&keyname, key_delimiter)))
|
318
|
+
#else
|
319
|
+
while ((part=strtok(keyname, key_delimiter)) && ((keyname=NULL)==NULL))
|
320
|
+
#endif
|
321
|
+
{
|
322
|
+
part2=strncpy(part2,part,128);
|
323
|
+
// debugprintf("- %s \n",part);
|
324
|
+
kmlptr=keymodlist;
|
325
|
+
while (kmlptr->name)
|
326
|
+
{
|
327
|
+
// debugprintf("-- %s %s \n", kmlptr->name, part);
|
328
|
+
if (!strcasecmp(kmlptr->name, part))
|
329
|
+
xev->state|=kmlptr->mask;
|
330
|
+
kmlptr++;
|
331
|
+
}
|
332
|
+
// debugprintf("--- %s \n",part);
|
333
|
+
}
|
334
|
+
// debugprintf("*** %s \n",part);
|
335
|
+
// debugprintf("*** %s \n",part2);
|
336
|
+
xev->keycode=XKeysymToKeycode(dpy,XStringToKeysym(part2));
|
337
|
+
debugprintf("state 0x%x, keycode 0x%x\n",xev->state, xev->keycode);
|
338
|
+
free(part2);
|
339
|
+
return ;
|
340
|
+
}
|
341
|
+
|
342
|
+
void sendfocus(Window w,int in_out)
|
343
|
+
{
|
344
|
+
XFocusChangeEvent focev;
|
345
|
+
|
346
|
+
focev.display=dpy;
|
347
|
+
focev.type=in_out;
|
348
|
+
focev.window=w;
|
349
|
+
focev.mode=NotifyNormal;
|
350
|
+
focev.detail=NotifyPointer;
|
351
|
+
XSendEvent(dpy,w,True,FocusChangeMask,(XEvent*)&focev);
|
352
|
+
XSync(dpy,True);
|
353
|
+
|
354
|
+
return;
|
355
|
+
}
|
356
|
+
|
357
|
+
void sendpointer_enter_or_leave(Window w,int in_out)
|
358
|
+
{
|
359
|
+
XCrossingEvent crossev;
|
360
|
+
crossev.type=in_out;
|
361
|
+
crossev.display=dpy;
|
362
|
+
crossev.window=w;
|
363
|
+
crossev.root=root;
|
364
|
+
crossev.subwindow=None;
|
365
|
+
crossev.time=fake_timestamp();
|
366
|
+
crossev.x=1;
|
367
|
+
crossev.y=1;
|
368
|
+
crossev.x_root=1;
|
369
|
+
crossev.y_root=1;
|
370
|
+
crossev.mode=NotifyNormal;
|
371
|
+
crossev.detail=NotifyNonlinear;
|
372
|
+
crossev.same_screen=True;
|
373
|
+
crossev.focus=True;
|
374
|
+
crossev.state=0;
|
375
|
+
XSendEvent(dpy,w,True,EnterWindowMask|LeaveWindowMask,(XEvent*)&crossev);
|
376
|
+
XSync(dpy,True);
|
377
|
+
return;
|
378
|
+
}
|
379
|
+
|
380
|
+
void sendkey(char *keyname,int x,int y,Window w,Window s)
|
381
|
+
{
|
382
|
+
make_key(keyname ,x,y,(XKeyEvent*)&xev);
|
383
|
+
xev.xkey.window=w;
|
384
|
+
xev.xkey.subwindow=s;
|
385
|
+
|
386
|
+
if (s) sendfocus(s,FocusIn);
|
387
|
+
|
388
|
+
XSendEvent(dpy,w,True,KeyPressMask,&xev);
|
389
|
+
xev.type = KeyRelease;
|
390
|
+
usleep(2000);
|
391
|
+
xev.xkey.time = fake_timestamp();
|
392
|
+
if (s) sendfocus(s,FocusOut);
|
393
|
+
XSendEvent(dpy,w,True,KeyReleaseMask,&xev);
|
394
|
+
XSync(dpy,True);
|
395
|
+
return;
|
396
|
+
}
|
397
|
+
|
398
|
+
void sendbutton(int button, int x, int y, Window w,Window s)
|
399
|
+
{
|
400
|
+
make_button(button,x,y,(XButtonEvent*)&xev);
|
401
|
+
xev.xbutton.window=w;
|
402
|
+
xev.xbutton.subwindow=s;
|
403
|
+
sendpointer_enter_or_leave(w,EnterNotify);
|
404
|
+
sendpointer_enter_or_leave(s,EnterNotify);
|
405
|
+
|
406
|
+
XSendEvent(dpy,w,True,ButtonPressMask,&xev);
|
407
|
+
XSync(dpy,True);
|
408
|
+
xev.type = ButtonRelease;
|
409
|
+
xev.xkey.state|=0x100;
|
410
|
+
usleep(1000);
|
411
|
+
xev.xkey.time = fake_timestamp();
|
412
|
+
XSendEvent(dpy,w,True,ButtonReleaseMask,&xev);
|
413
|
+
sendpointer_enter_or_leave(s,LeaveNotify);
|
414
|
+
sendpointer_enter_or_leave(w,LeaveNotify);
|
415
|
+
XSync(dpy,True);
|
416
|
+
|
417
|
+
return;
|
418
|
+
}
|
419
|
+
|
420
|
+
|
421
|
+
int check(char *s)
|
422
|
+
{
|
423
|
+
int d;
|
424
|
+
char *buffer;
|
425
|
+
|
426
|
+
buffer=malloc(strlen(s));
|
427
|
+
if(buffer==NULL)
|
428
|
+
{
|
429
|
+
fprintf(stderr,"%s: out of memory\n",progname);
|
430
|
+
return 0;
|
431
|
+
}
|
432
|
+
|
433
|
+
if(2!=sscanf(s,"Key %s Focus %s %s",buffer,buffer,buffer) &&
|
434
|
+
2!=sscanf(s,"Key %s WindowID %i %s",buffer,&d,buffer) &&
|
435
|
+
2!=sscanf(s,"Key %s Focus WindowID %i %s",buffer,&d,buffer) &&
|
436
|
+
2!=sscanf(s,"Key %s %s %s",buffer,buffer,buffer) &&
|
437
|
+
4!=sscanf(s,"Button %d %d %d Focus %s %s",&d,&d,&d,buffer,buffer) &&
|
438
|
+
4!=sscanf(s,"Button %d %d %d WindowID %i %s",&d,&d,&d,&d,buffer) &&
|
439
|
+
4!=sscanf(s,"Button %d %d %d Focus WindowID %i %s",&d,&d,&d,&d,buffer) &&
|
440
|
+
4!=sscanf(s,"Button %d %d %d %s %s",&d,&d,&d,buffer,buffer) &&
|
441
|
+
4!=sscanf(s,"xy_Key %d %d %s Focus %s %s",&d,&d,buffer,buffer,buffer) &&
|
442
|
+
4!=sscanf(s,"xy_Key %d %d %s WindowID %i %s",&d,&d,buffer,&d,buffer) &&
|
443
|
+
4!=sscanf(s,"xy_Key %d %d %s Focus WindowID %i %s",&d,&d,buffer,&d,buffer) &&
|
444
|
+
4!=sscanf(s,"xy_Key %d %d %s %s",&d,&d,buffer,buffer))
|
445
|
+
{
|
446
|
+
fprintf(stderr,"%s: bad config string \"%s\"\n",progname,s);
|
447
|
+
free(buffer);
|
448
|
+
return 0;
|
449
|
+
}
|
450
|
+
free(buffer);
|
451
|
+
return 1;
|
452
|
+
}
|
453
|
+
|
454
|
+
void xevent_init()
|
455
|
+
{
|
456
|
+
dpy=XOpenDisplay(NULL);
|
457
|
+
if(dpy==NULL) {
|
458
|
+
fprintf(stderr,"Can't open DISPLAY.\n");
|
459
|
+
}
|
460
|
+
root=RootWindow(dpy,DefaultScreen(dpy));
|
461
|
+
}
|
462
|
+
|
463
|
+
int xevent(char *cmd)
|
464
|
+
{
|
465
|
+
char keyname[128];
|
466
|
+
int pointer_button,pointer_x,pointer_y;
|
467
|
+
char windowname[64];
|
468
|
+
int WindowID;
|
469
|
+
|
470
|
+
*windowname=0;
|
471
|
+
|
472
|
+
if(2==sscanf(cmd,"Key %s Focus WindowID %i",keyname,&WindowID) ||
|
473
|
+
4==sscanf(cmd,"Button %d %d %d Focus WindowID %i",
|
474
|
+
&pointer_button,&pointer_x,&pointer_y,&WindowID) ||
|
475
|
+
4==sscanf(cmd,"xy_Key %d %d %s Focus WindowID %i",
|
476
|
+
&pointer_x,&pointer_y,keyname,&WindowID) ||
|
477
|
+
2==sscanf(cmd,"Key %s Focus %s",keyname,windowname) ||
|
478
|
+
4==sscanf(cmd,"Button %d %d %d Focus %s",
|
479
|
+
&pointer_button,&pointer_x,&pointer_y,windowname) ||
|
480
|
+
4==sscanf(cmd,"xy_Key %d %d %s Focus %s",
|
481
|
+
&pointer_x,&pointer_y,keyname,windowname))
|
482
|
+
{
|
483
|
+
debugprintf("Focus\n");
|
484
|
+
/* focussed ? */
|
485
|
+
if(*windowname) {
|
486
|
+
WindowID=find_window_focused(root,windowname);
|
487
|
+
if(!WindowID) {
|
488
|
+
debugprintf("target window '%s' doesn't have focus\n",windowname);
|
489
|
+
return -1;
|
490
|
+
}
|
491
|
+
debugprintf("focused: %s\n",windowname);
|
492
|
+
} else {
|
493
|
+
Window cur;
|
494
|
+
int tmp;
|
495
|
+
|
496
|
+
XGetInputFocus(dpy, &cur, &tmp);
|
497
|
+
if(WindowID != cur) {
|
498
|
+
debugprintf("target window '0x%x' doesn't have focus\n",WindowID);
|
499
|
+
return -1;
|
500
|
+
}
|
501
|
+
debugprintf("focused: 0x%x\n",WindowID);
|
502
|
+
}
|
503
|
+
} else if(2==sscanf(cmd,"Key %s WindowID %i",keyname,&WindowID) ||
|
504
|
+
4==sscanf(cmd,"Button %d %d %d WindowID %i",
|
505
|
+
&pointer_button,&pointer_x,&pointer_y,&WindowID) ||
|
506
|
+
4==sscanf(cmd,"xy_Key %d %d %s WindowID %i",
|
507
|
+
&pointer_x,&pointer_y,keyname,&WindowID)) {
|
508
|
+
debugprintf("WindowID: 0x%x\n",WindowID);
|
509
|
+
/* WindowID passed */
|
510
|
+
} else if(2==sscanf(cmd,"Key %s %s",keyname,windowname) ||
|
511
|
+
4==sscanf(cmd,"Button %d %d %d %s",
|
512
|
+
&pointer_button,&pointer_x,&pointer_y,windowname) ||
|
513
|
+
4==sscanf(cmd,"xy_Key %d %d %s %s\n",
|
514
|
+
&pointer_x,&pointer_y,keyname,windowname)) {
|
515
|
+
debugprintf("name: %s\n",windowname);
|
516
|
+
WindowID=find_window(root,windowname);
|
517
|
+
}
|
518
|
+
|
519
|
+
switch(cmd[0])
|
520
|
+
{
|
521
|
+
case 'K': // Key
|
522
|
+
debugprintf("keyname: %s \t WindowID: 0x%x\n",keyname,WindowID);
|
523
|
+
debugprintf("%s\n",cmd);
|
524
|
+
sendkey(keyname,1,1,(Window)WindowID,0);
|
525
|
+
break;
|
526
|
+
|
527
|
+
case 'B': // Button
|
528
|
+
case 'x': // xy_Key
|
529
|
+
subw=find_sub_window(root,windowname,&pointer_x,&pointer_y);
|
530
|
+
if(subw) {
|
531
|
+
if (WindowID==subw) subw=0;
|
532
|
+
debugprintf("%s\n",cmd);
|
533
|
+
switch(cmd[0])
|
534
|
+
{
|
535
|
+
case 'B':
|
536
|
+
sendbutton(pointer_button,pointer_x,pointer_y,WindowID,subw);
|
537
|
+
break;
|
538
|
+
case 'x':
|
539
|
+
sendkey(keyname,pointer_x,pointer_y,WindowID,subw);
|
540
|
+
break;
|
541
|
+
}
|
542
|
+
}
|
543
|
+
break;
|
544
|
+
|
545
|
+
}
|
546
|
+
return 1;
|
547
|
+
}
|
548
|
+
|
549
|
+
|
550
|
+
/*
|
551
|
+
* Ruby stuff
|
552
|
+
*/
|
553
|
+
|
554
|
+
VALUE cXevent;
|
555
|
+
|
556
|
+
/*
|
557
|
+
void xevent_rb_new(VALUE class) {
|
558
|
+
xevent_init();
|
559
|
+
}
|
560
|
+
*/
|
561
|
+
|
562
|
+
VALUE xevent_rb_do(VALUE self, VALUE cmd) {
|
563
|
+
xevent_init();
|
564
|
+
xevent(RSTRING(cmd)->ptr);
|
565
|
+
return self;
|
566
|
+
}
|
567
|
+
|
568
|
+
void Init_xevent() {
|
569
|
+
cXevent = rb_define_class("Xevent", rb_cObject);
|
570
|
+
//rb_define_singleton_method(cXevent, "new", xevent_rb_new, 0);
|
571
|
+
rb_define_method(cXevent, "do", xevent_rb_do, 1);
|
572
|
+
}
|
573
|
+
|
574
|
+
|