rlirc 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|