Skip to content

Commit 140d716

Browse files
committed
Example tool for owning a selection (very stupid)
1 parent 05fc7ac commit 140d716

File tree

2 files changed

+171
-1
lines changed

2 files changed

+171
-1
lines changed
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python
22
#
3-
# examples/selection.py -- demonstrate using selections (clipboard)
3+
# examples/get_selection.py -- demonstrate getting selections
4+
# (equivalent to pasting from the clipboard)
45
#
56
# Copyright (C) 2013 Peter Liljenberg <peter.liljenberg@gmail.com>
67
#
@@ -67,6 +68,8 @@ def main():
6768
# Create ourselves a window and a property for the returned data
6869
w = d.screen().root.create_window(
6970
0, 0, 10, 10, 0, X.CopyFromParent)
71+
w.set_wm_name(os.path.basename(sys.argv[0]))
72+
7073
data_atom = d.get_atom('SEL_DATA')
7174

7275
# The data_atom should not be set according to ICCCM, and since

examples/put_selection.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#!/usr/bin/env python
2+
#
3+
# examples/put_selection.py -- demonstrate putting selections
4+
# (equivalent to copying to the clipboard)
5+
#
6+
# Copyright (C) 2013 Peter Liljenberg <peter.liljenberg@gmail.com>
7+
#
8+
# This program is free software; you can redistribute it and/or modify
9+
# it under the terms of the GNU General Public License as published by
10+
# the Free Software Foundation; either version 2 of the License, or
11+
# (at your option) any later version.
12+
#
13+
# This program is distributed in the hope that it will be useful,
14+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
# GNU General Public License for more details.
17+
#
18+
# You should have received a copy of the GNU General Public License
19+
# along with this program; if not, write to the Free Software
20+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21+
22+
import sys
23+
import os
24+
25+
# Change path so we find Xlib
26+
sys.path.insert(1, os.path.join(sys.path[0], '..'))
27+
28+
from Xlib import X, display, Xutil, Xatom
29+
from Xlib.protocol import event
30+
31+
32+
# This script does not implement fancy stuff like INCR or MULTIPLE, so
33+
# put a hard limit on the amount of data we allow sending this way
34+
MAX_SIZE = 50000
35+
36+
37+
def log(msg, *args):
38+
sys.stderr.write(msg.format(*args) + '\n')
39+
40+
def error(msg, *args):
41+
log(msg, *args)
42+
sys.exit(1)
43+
44+
45+
def main():
46+
if len(sys.argv) < 3 or len(sys.argv) > 4:
47+
sys.exit('usage: {0} SELECTION TYPE [FILE]\n\n'
48+
'SELECTION is typically PRIMARY, SECONDARY or CLIPBOARD.\n'
49+
'If FILE is omitted, stdin is read.\n'
50+
.format(sys.argv[0]))
51+
52+
if len(sys.argv) > 3:
53+
f = open(sys.argv[3], 'rb')
54+
else:
55+
f = sys.stdin
56+
57+
# Limit selection size to avoid implementing INCR
58+
data = f.read(MAX_SIZE)
59+
f.close()
60+
61+
if len(data) >= MAX_SIZE:
62+
log('too much data: {0} bytes, max is {1} bytes', len(data), MAX_SIZE)
63+
error('INCR support not implemented to put larger files')
64+
65+
66+
d = display.Display()
67+
68+
sel_name = sys.argv[1]
69+
sel_atom = d.get_atom(sel_name)
70+
71+
type_name = sys.argv[2]
72+
type_atom = d.get_atom(type_name)
73+
74+
targets_atom = d.get_atom('TARGETS')
75+
76+
77+
# We must have a window to own a selection
78+
w = d.screen().root.create_window(
79+
0, 0, 10, 10, 0, X.CopyFromParent)
80+
81+
# And to grab the selection we must have a timestamp, get one with
82+
# a property notify when we're anyway setting wm_name
83+
w.change_attributes(event_mask = X.PropertyChangeMask)
84+
w.set_wm_name(os.path.basename(sys.argv[0]))
85+
86+
e = d.next_event()
87+
sel_time = e.time
88+
w.change_attributes(event_mask = 0)
89+
90+
# Grab the selection and make sure we actually got it
91+
w.set_selection_owner(sel_atom, sel_time)
92+
if d.get_selection_owner(sel_atom) != w:
93+
log('could not take ownership of {0}', sel_name)
94+
return
95+
96+
log('took ownership of selection {0}', sel_name)
97+
98+
# The event loop, waiting for and processing requests
99+
while True:
100+
e = d.next_event()
101+
102+
if (e.type == X.SelectionRequest
103+
and e.owner == w
104+
and e.selection == sel_atom):
105+
106+
client = e.requestor
107+
108+
if e.property == X.NONE:
109+
log('request from obsolete client!')
110+
client_prop = e.target # per ICCCM recommendation
111+
else:
112+
client_prop = e.property
113+
114+
target_name = d.get_atom_name(e.target)
115+
116+
log('got request for {0}, dest {1} on 0x{2:08x} {3}',
117+
target_name, d.get_atom_name(client_prop),
118+
client.id, client.get_wm_name())
119+
120+
# Is the client asking for which types we support?
121+
if e.target == targets_atom:
122+
# Then respond with TARGETS and the type
123+
prop_value = [targets_atom, type_atom]
124+
prop_type = Xatom.ATOM
125+
prop_format = 32
126+
127+
# Request for the offered type
128+
elif e.target == type_atom:
129+
prop_value = data
130+
prop_type = type_atom
131+
prop_format = 8
132+
133+
# Something else, tell client they can't get it
134+
else:
135+
log('refusing conversion to {0}', target_name)
136+
client_prop = X.NONE
137+
138+
# Put the data on the dest window, if possible
139+
if client_prop != X.NONE:
140+
client.change_property(
141+
client_prop, prop_type, prop_format, prop_value)
142+
143+
# And always send a selection notification
144+
ev = event.SelectionNotify(
145+
time = e.time,
146+
requestor = e.requestor,
147+
selection = e.selection,
148+
target = e.target,
149+
property = client_prop)
150+
151+
client.send_event(ev)
152+
153+
# Done!
154+
155+
elif (e.type == X.SelectionClear
156+
and e.window == w
157+
and e.atom == sel_atom):
158+
log('lost ownership of selection {0}', sel_name)
159+
return
160+
161+
# A proper owner would also look for PropertyNotify here on
162+
# the selector's windows to implement INCR and waiting for
163+
# acknowledgement that the client has finished copying.
164+
165+
166+
if __name__ == '__main__':
167+
main()

0 commit comments

Comments
 (0)