PDA

View Full Version : Window Rules Helper


contrast
September 12th, 2007, 07:18 PM
This is just a little script I threw together because I got tired of having to refer to the how-to every time I needed to know something about a window. (Thanks to Jupiter for posting the commands I used in this.) Just save the below text into a file in your path (remove the .txt extension, obviously) and make it executable.

delfick
September 13th, 2007, 11:48 AM
very nice, thnx :D

(this should be integrated into ccsm somehow...)

Belgabor
September 13th, 2007, 01:32 PM
Could you add an option that gives the user all options with one click? Something like a processed xprop output without all the confusing, unnecessary crap.

contrast
September 13th, 2007, 07:47 PM
I was actually already trying to figure out how to do that. I just now got it (see orig. post), but the output's not very pretty, i.e., I don't yet know how to remove the newlines from it. Check the new screenshot to see what I mean.

emic
September 13th, 2007, 08:24 PM
Found this on my hd ;)

#! /bin/bash
# Beryl Property Finder 1.1
# 2007-01-04
#
# Uses code from
# http://wiki.beryl-project.org/wiki/Plugins/Set_Window_Attributes
#

tmpFile=$(mktemp)

echo "Detecting all attributes"
echo "Click on a window"
echo

xprop >${tmpFile}

winType=$(grep 'WINDOW_TYPE' ${tmpFile} |sed 's/^.*_\([^_]*\)$/\1/')
winClass=$(grep '^WM_CLASS' ${tmpFile} |cut -d\" -f4)
className=$(grep '^WM_CLASS' ${tmpFile} |cut -d\" -f2)
winTitle=$(grep '^WM_NAME(STRING)' ${tmpFile} |cut -d\" -f2)
owningProg=$(grep '^_NET_WM_PID(CARDINAL)' ${tmpFile} |sed 's/.*= //g' |\
xargs ps -o comm= -p)
classRole=$(grep '^WM_WINDOW_ROLE(STRING)' ${tmpFile} |cut -d\" -f2)

rm -f ${tmpFile}

cat <<EOF
window type ${winType}
window class ${winClass}
class name ${className}
window title ${winTitle}
owning program ${owningProg}
class role ${classRole}
EOF

exit 0
Hth

It produces some crappy output on windows with no NET_WM_PID

Belgabor
September 14th, 2007, 02:55 AM
Nice, thanks. Works like a charm.

Moses
September 14th, 2007, 04:23 PM
I use the following Python script. It's always nice with a GUI!


#!/usr/bin/env python

import gtk
import re
import os


UNQUOTE_RE = re.compile('"(.*?)"')
TYPE_MAPPINGS = {
'_NET_WM_WINDOW_TYPE_DESKTOP': 'Desktop',
'_NET_WM_WINDOW_TYPE_DOCK': 'Dock',
'_NET_WM_WINDOW_TYPE_TOOLBAR': 'Toolbar',
'_NET_WM_WINDOW_TYPE_MENU': 'Menu',
'_NET_WM_WINDOW_TYPE_UTILITY': 'Utility',
'_NET_WM_WINDOW_TYPE_SPLASH': 'Splash',
'_NET_WM_WINDOW_TYPE_DIALOG': 'Dialog',
'_NET_WM_WINDOW_TYPE_DROPDOWN_MENU': 'DropDownMenu',
'_NET_WM_WINDOW_TYPE_POPUP_MENU': 'Popup',
'_NET_WM_WINDOW_TYPE_TOOLTIP': 'Tooltip',
'_NET_WM_WINDOW_TYPE_NOTIFICATION': 'Notification',
'_NET_WM_WINDOW_TYPE_COMBO': 'Combo',
'_NET_WM_WINDOW_TYPE_DND': 'DnD',
'_NET_WM_WINDOW_TYPE_NORMAL': 'Normal'}
UNKNOWN_VALUE = '---'


def find_prop(xprop_data, prop_name):
raw_value = None
for line in xprop_data:
if line[0].find(prop_name) > -1:
return line[1]
return UNKNOWN_VALUE


def find_type(xprop_data):
value = find_prop(xprop_data, 'WM_WINDOW_TYPE')
if not value or not value in TYPE_MAPPINGS:
return 'Unknown'
else:
return TYPE_MAPPINGS[value]


def find_role(xprop_data):
value = find_prop(xprop_data, 'WM_WINDOW_ROLE')
if value:
match = UNQUOTE_RE.findall(value)
if match:
return match[0]
return UNKNOWN_VALUE


def find_name(xprop_data):
value = find_prop(xprop_data, 'WM_CLASS')
if value:
matches = UNQUOTE_RE.findall(value)
return matches[0]
return UNKNOWN_VALUE


def find_class(xprop_data):
value = find_prop(xprop_data, 'WM_CLASS')
if value:
matches = UNQUOTE_RE.findall(value)
return matches[1]
return UNKNOWN_VALUE


def find_title(xprop_data):
value = find_prop(xprop_data, 'WM_NAME')
if value:
matches = UNQUOTE_RE.findall(value)
return matches[0]
return UNKNOWN_VALUE


properties = [
['Type:', gtk.Label(), find_type],
['Role:', gtk.Label(), find_role],
['Name:', gtk.Label(), find_name],
['Class:', gtk.Label(), find_class],
['Title:', gtk.Label(), find_title]
]


def on_win_delete_event(widget, event):
gtk.main_quit()


def on_get_properties_button_clicked(widget):
# Use xprop to get data
f = os.popen('xprop')
xprop_data = [[t.strip() for t in line.split(' = ')] \
for line in f.readlines()]

# Update GUI
for row in properties:
row[1].set_text(row[2](xprop_data))

f.close()


def on_close_button_clicked(widget):
gtk.main_quit()


def main():
# Create GUI
global properties

win = gtk.Window()
win.set_position(gtk.WIN_POS_MOUSE)
win.set_resizable(False)
win.set_title('WinRules Helper')
win.connect('delete-event', on_win_delete_event)

table = gtk.Table(rows = 6, columns = 2)

for i, row in enumerate(properties):
caption_label = gtk.Label()
caption_label.set_markup('<b>%s</b>' % row[0])
caption_label.set_alignment(0.0, 0.0)
table.attach(caption_label, 0, 1, i, i + 1,
xoptions = gtk.FILL,
yoptions = gtk.FILL,
xpadding = 5,
ypadding = 0)

row[1].set_text(UNKNOWN_VALUE)
row[1].set_alignment(0.0, 0.0)
table.attach(row[1], 1, 2, i, i + 1,
xpadding = 5,
ypadding = 0)

box = gtk.HButtonBox()
box.set_border_width(5)
box.set_layout(gtk.BUTTONBOX_END)
box.set_spacing(10)
table.attach(box, 0, 2, 5, 6,
xoptions = gtk.FILL,
yoptions = gtk.FILL,
xpadding = 5,
ypadding = 5)

get_properties_button = gtk.Button('Get properties')
get_properties_button.connect('clicked', on_get_properties_button_clicked)
box.add(get_properties_button)

close_button = gtk.Button(stock = 'gtk-close')
close_button.connect('clicked', on_close_button_clicked)
box.add(close_button)

win.add(table)
win.show_all()

# Get window properties
on_get_properties_button_clicked(get_properties_bu tton)

gtk.main()

if __name__ == '__main__':
main()

contrast
September 17th, 2007, 03:55 PM
Changed it yet again. It no longer asks whether you want to know one thing or everything about a window, and you can now tell it to wait however many seconds before it grabs the cursor (e.g., if you run "win-rules-helper.sh -w 10", it will sleep 10 seconds, then run xprop).

contrast
September 24th, 2007, 08:21 PM
Updated to 0.0.5. This will probably be the last update, unless I get any feature requests that are within the scope of my very limited abilities. :-P

Changes:
Made the output suitable for copying and pasting straight into CCSM (except the Type criteria, which always comes out in all caps - I might correct this if I learn how to efficiently use SED in the near future)
Added an option to launch Jupiter's tutorial in Firefox (-h)
Added an option to show a usage message (-u)

As always, comments, suggestions and nitpicks are welcome.

revertex
October 20th, 2007, 08:00 PM
@ moses:

can't get it to work, a pity.

<code> ./gwindowid
Traceback (most recent call last):
File "./gwindowid", line 161, in ?
main()
File "./gwindowid", line 156, in main
on_get_properties_button_clicked(get_properties_bu tton)
File "./gwindowid", line 97, in on_get_properties_button_clicked
row[1].set_text(row[2](xprop_data))
File "./gwindowid", line 72, in find_title
return matches[0]
IndexError: list index out of range </code>

@contrast,

thank you, I was planning to write a script, you save my time, your script is exactly what I'm looking for.

maybe a GUI with zenity can be more fun.

Moses
October 21st, 2007, 12:46 AM
Whoops, no error checking! I forgot to update the version here on the forum. Here's what I use now:


#!/usr/bin/env python

import gobject
import gtk
import re
import os


UNQUOTE_RE = re.compile('"(.*?)"')
TYPE_MAPPINGS = {
'_NET_WM_WINDOW_TYPE_DESKTOP': 'Desktop',
'_NET_WM_WINDOW_TYPE_DOCK': 'Dock',
'_NET_WM_WINDOW_TYPE_TOOLBAR': 'Toolbar',
'_NET_WM_WINDOW_TYPE_MENU': 'Menu',
'_NET_WM_WINDOW_TYPE_UTILITY': 'Utility',
'_NET_WM_WINDOW_TYPE_SPLASH': 'Splash',
'_NET_WM_WINDOW_TYPE_DIALOG': 'Dialog',
'_NET_WM_WINDOW_TYPE_DROPDOWN_MENU': 'DropDownMenu',
'_NET_WM_WINDOW_TYPE_POPUP_MENU': 'Popup',
'_NET_WM_WINDOW_TYPE_TOOLTIP': 'Tooltip',
'_NET_WM_WINDOW_TYPE_NOTIFICATION': 'Notification',
'_NET_WM_WINDOW_TYPE_COMBO': 'Combo',
'_NET_WM_WINDOW_TYPE_DND': 'DnD',
'_NET_WM_WINDOW_TYPE_NORMAL': 'Normal'}
UNKNOWN_VALUE = '---'
TIMER_DURATION = 3000
TIMER_RESOLUTION = 1000


def find_prop(xprop_data, prop_name):
raw_value = None
for line in xprop_data:
if line[0].find(prop_name) > -1:
return line[1]
return UNKNOWN_VALUE


def find_type(xprop_data):
value = find_prop(xprop_data, 'WM_WINDOW_TYPE')
if not value or not value in TYPE_MAPPINGS:
return 'Unknown'
else:
return TYPE_MAPPINGS[value]


def find_role(xprop_data):
value = find_prop(xprop_data, 'WM_WINDOW_ROLE')
if value:
match = UNQUOTE_RE.findall(value)
if match and len(match) > 0:
return match[0]
return UNKNOWN_VALUE


def find_name(xprop_data):
value = find_prop(xprop_data, 'WM_CLASS')
if value:
matches = UNQUOTE_RE.findall(value)
if matches and len(matches) > 0:
return matches[0]
return UNKNOWN_VALUE


def find_class(xprop_data):
value = find_prop(xprop_data, 'WM_CLASS')
if value:
matches = UNQUOTE_RE.findall(value)
if matches and len(matches) > 0:
return matches[1]
return UNKNOWN_VALUE


def find_title(xprop_data):
value = find_prop(xprop_data, 'WM_NAME')
if value:
matches = UNQUOTE_RE.findall(value)
if matches and len(matches) > 0:
return matches[0]
return UNKNOWN_VALUE


properties = [
['Type:', gtk.Label(), find_type],
['Role:', gtk.Label(), find_role],
['Name:', gtk.Label(), find_name],
['Class:', gtk.Label(), find_class],
['Title:', gtk.Label(), find_title]
]
controls = {
'status_label': gtk.Label(' '),
'get_properties_button': gtk.Button('Get properties')}

countdown = 0


def get_properties():
# Use xprop to get data
f = os.popen('xprop')
xprop_data = [[t.strip() for t in line.split(' = ')] \
for line in f.readlines()]

if f.close():
controls['status_label'].set_markup('<i>Failed to get properties!</i>')
return False

# Update GUI
for row in properties:
row[1].set_text(row[2](xprop_data))

return False


def on_timeout():
global countdown

countdown = countdown - 1
if countdown == 0:
controls['get_properties_button'].set_sensitive(True)
controls['status_label'].set_text(' ')
gobject.idle_add(get_properties)
return False
else:
controls['status_label'].set_text(str(countdown))
return True


def on_win_delete_event(widget, event):
gtk.main_quit()


def on_get_properties_button_clicked(widget):
global countdown

countdown = TIMER_DURATION / TIMER_RESOLUTION
controls['status_label'].set_text(str(countdown))
gobject.timeout_add(TIMER_RESOLUTION, on_timeout)
controls['get_properties_button'].set_sensitive(False)


def on_close_button_clicked(widget):
gtk.main_quit()


def main():
# Create GUI
global properties

win = gtk.Window()
win.set_position(gtk.WIN_POS_MOUSE)
win.set_resizable(False)
win.set_title('WinRules Helper')
win.connect('delete-event', on_win_delete_event)

table = gtk.Table(rows = 7, columns = 2)

for i, row in enumerate(properties):
caption_label = gtk.Label()
caption_label.set_markup('<b>%s</b>' % row[0])
caption_label.set_alignment(0.0, 0.0)
table.attach(caption_label, 0, 1, i, i + 1,
xoptions = gtk.FILL,
yoptions = gtk.FILL,
xpadding = 5,
ypadding = 0)

row[1].set_text(UNKNOWN_VALUE)
row[1].set_alignment(0.0, 0.0)
table.attach(row[1], 1, 2, i, i + 1,
xpadding = 5,
ypadding = 0)

table.attach(controls['status_label'], 0, 2, 5, 6,
xoptions = gtk.FILL,
yoptions = gtk.FILL,
xpadding = 5,
ypadding = 5)

box = gtk.HButtonBox()
box.set_border_width(5)
box.set_layout(gtk.BUTTONBOX_END)
box.set_spacing(10)
table.attach(box, 0, 2, 6, 7,
xoptions = gtk.FILL,
yoptions = gtk.FILL,
xpadding = 5,
ypadding = 5)

get_properties_button = controls['get_properties_button']
get_properties_button.connect('clicked', on_get_properties_button_clicked)
box.add(get_properties_button)

close_button = gtk.Button(stock = 'gtk-close')
close_button.connect('clicked', on_close_button_clicked)
box.add(close_button)

win.add(table)
win.show_all()

# Get window properties
get_properties()

gtk.main()

if __name__ == '__main__':
main()