Tips and Tricks About Computers, Web Development, Linux, the Internet and the Like
Python
Python Multi-head X (Nvidia TwinView / Dual Monitor) Development Notes
Jul 5th
Posted by Tyler Mulligan in Application Management
Preface
The following development notes were written after researching the underlying handling of dual monitors in the X window system on Linux. I’ve included a code snippet that I built to help demonstrate behavior and create a proof of concept to show I can determine which monitor a window is on using only python and no statically set coordinates.
Introduction
I mentioned in my previous post that I’m using an nVidia video card with “TwinView” software that outputs my video as if it were one screen, which it technically is, one X screen. This means that the distinction between monitors is not mapped in the X tree, it’s handled by the window manager. Unlike Xinerama, which has an x session per monitor and stitches them together. Xinerama, however, has is being deprecated in favor of RandR but regardless, TwinView is my choice and is not an option for me to change to.
With all of that said, the decision to bridge python to c that interfaces with compiz is a deadend and would be better implemented based on the new 0.9.0 C++ api. It would be nice to be able to return a list of windows from a “monitor” object. However, that’s beyond my current scope. I was able to whip up some python that to show that implementing the monitor management in python using the gtk module isn’t /that/ hacky. I emphasize because I read some posts that claimed window decorations could be an issue in accurate calculations.
Some Code
Read the comment on line 2. Learn more about pygtk here
#!/usr/bin/python # Print some information about the X environment, the monitor setup, currently active window and cursor position import gtk.gdk screen = gtk.gdk.screen_get_default() print "X default screen size: %d x %d" % (screen.get_width(), screen.get_height()) print "xid of root window: %d" % screen.get_root_window().xid monitors = int(screen.get_n_monitors()) print "== %d monitors ==" % monitors for m in range(0, monitors): print " - geometry of monitor %d: %s" % (m, screen.get_monitor_geometry(m)) window = screen.get_active_window() win_x, win_y, win_w, win_h, win_bit_depth = window.get_geometry() print "active window on monitor: %d" % screen.get_monitor_at_point((win_x+(win_w/2)),(win_y+(win_h/2))) print "window geometry (x,y,w,h): %d, %d, %d, %d" % (win_x,win_y,win_w,win_h) display = gtk.gdk.display_get_default() pointer = display.get_pointer() print "cursor position (x, y): %d, %d" % (pointer[1], pointer[2]) print "cursor on monitor: %d" % screen.get_monitor_at_point(pointer[1],pointer[2])
thanks to those in #compiz-dev and #python on freenode who helped me come around to create this snippet. I hope it will help others looking to develop for multi-head setups in Linux. Please let me know if I missed anything or did something incorrectly, this is new territory for me.
New Python eBook, Much Better, Down and Dirty
Jul 1st
Posted by Tyler Mulligan in Books
Some friends recommended a book that’s a quicker pace with some better programming practices. Learn Python The Hard way (or quick way
). It reads more like a walk-through tutorial / quick reference and gives you easily repeatable programming practices that will get you a stronger feel for the language. It’s probably best used in combination with the Python Documentation. Also, a fair warning, Dive Into Python has war declared against it.
scp-notifications for GNOME and Ubuntu – Expanding on My Original Python Script
Jun 26th
Posted by Tyler Mulligan in Compiz
I’ve only been coding Python for ~12 hours total, so don’t expect this to be perfect. Knowing what I know about creating/testing other software and doing my best to scour through very light pynotify documentation, I’ve begun to build out this script to be more useful / portable / configurable. As the Version 0.6 indicates, I’m not quite at my goal yet and there is still more to learn to bring it up to that point.
I’m releasing this early on my blog just in case I caught any people yesterday who’ve been experimenting with my research / code so far. I’ll share it on github when I evolve it just a bit more.
#!/usr/bin/env python # # Title: scp-notifications # Author: Tyler Mulligan (tyler@detrition.net) # Date: 06/26/2010 # Version: 0.6 # Description: # Used in combination with an event, such as an action or cronjob, this script # will scp the latest file from a folder to your server. # # Optionally, it can copy the url to your clipboard and/or show a popup with a # link to the file after succesfully uploading # # Orginally developed to be piped to from compiz screenshot tool # http://interwebninja.com/videos/compiz-screenshot-piped-to-notification-daemon-for-upload.ogv # # The MIT License # # Copyright (c) 2010 Tyler Mulligan # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE # import pygtk pygtk.require('2.0') import pynotify import gtk import sys import os import subprocess # Set Variables ##################################### user = "user" host = "server.com" # All should have trailing slashes lfolder = "/home/user/screenshots/" hfolder = "/home/remote_user/screenshots/" httplink = "http://"+host+"/screenshots/" # Display ##################################### t1 = 5000 # timeout for screenshot upload dialog t2 = 3000 # timeout for screenshot preview t3 = 7000 # timeout for link dialog screenshot_preview = 1 # If using with the compiz screenshot plugin, you may want this popup_link = 1 # another popup copy_to_clipboard = 0 # automatically copy text to keyboard # Position ##################################### # Get screensize < < used for relative positioning display = gtk.gdk.display_get_default() screen = display.get_default_screen() x = screen.get_width() - 1 y = screen.get_height() - 1 # 0 for Automatic Placement """ x1 = 0 y1 = 0 x2 = 0 y2 = 0 x3 = 0 y3 = 0 """ # Define Relative Position (assuming top-right) x1 = x-1 y1 = 12 x2 = x1 y2 = y1 + 100 x3 = x-1 y3 = 12 # Define Static (1920 puts it on my second monitor) """ x1 = 1919 y1 = 12 x2 = x1 y2 = y1 + 100 x3 = 1920 y3 = 12 """ ##################################### def upload_cb(n, action): assert action == "upload" subprocess.call(["scp", os.path.join(lfolder, f), '%s@%s:%s' % (user, host, hfolder)]) # setup URL in if copy_to_clipboard: clipboard = gtk.clipboard_get() clipboard.set_text(httplink + f) # make our data available to other applications clipboard.store() # Notification: Link for the clicking if popup_link: n3 = pynotify.Notification("Here is your link","<a href='" + httplink + f + "'>" + httplink + f + "") helper = gtk.Button() icon = helper.render_icon(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_DIALOG) n3.set_icon_from_pixbuf(icon) n3.set_urgency(pynotify.URGENCY_NORMAL) if x3: n3.set_hint("x", x3) if y3: n3.set_hint("y", y3) n3.set_timeout(t3) n3.connect("closed",closen3_cb) if not n3.show(): print "Failed to send notification" sys.exit(1) closen1_cb(n1) closen2_cb(n2) gtk.main_quit() sys.exit(1) # Notification 1 was closed def closen1_cb(n): n1.close() if screenshot_preview: n2.close() gtk.main_quit() # Notification 2 was closed def closen2_cb(n): n2.close() gtk.main_quit() # Notification 2 was closed def closen3_cb(n): n3.close() gtk.main_quit() # The Ignore button was clicked def ignore_cb(n, action): assert action == "ignore" closen1_cb(n1) closen2_cb(n2) gtk.main_quit() # Main def main(): gtk.main() # Init if __name__ == '__main__': if not pynotify.init("Notifier 'scp' Option"): sys.exit(1) # Get latest file and build uri start = os.path.abspath(lfolder) f = max([(os.path.getmtime(os.path.join(start,p)),p) for p in os.listdir(start)])[1] uri = lfolder + f # Notification: Upload to Server n1 = pynotify.Notification("Upload to Server?","Copy the file '" + f + "' to the server?") helper = gtk.Button() icon = helper.render_icon(gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_DIALOG) n1.set_icon_from_pixbuf(icon) n1.set_urgency(pynotify.URGENCY_NORMAL) if x1: n1.set_hint("x", x1) if y1: n1.set_hint("y", y1) n1.set_timeout(t1) n1.add_action("upload", "Yes, Upload", upload_cb) n1.add_action("ignore", "Ignore", ignore_cb) n1.connect("closed",closen1_cb) if not n1.show(): print "Failed to send notification" sys.exit(1) # Notification: Screenshot Preview if screenshot_preview: n2 = pynotify.Notification("Screenshot Preview", "", uri) n2.set_urgency(pynotify.URGENCY_LOW) if x2: n2.set_hint("x", x2) if y2: n2.set_hint("y", y2) n2.set_timeout(t2) n2.connect("closed",closen2_cb) if not n2.show(): print "Failed to send notification" sys.exit(1) main()
I also rigged up a quick gallery script to parse the incoming images http://interwebninja.com/screenshots/ — I used AutoGeneratingGallery for this.
Ubuntu Notifications (osd-notify) Sucks, notifications-daemon Rocks – Exploiting the Goodness with Compiz
Jun 25th
Posted by Tyler Mulligan in Compiz
Introduction
The long name for this blog used to be “Tyler Mulligan’s Tips and Tricks for Increasing your Efficiency“. I love finding ways to increase my efficiency and let computers do the work while I focus on more interests aspects of what the computer is providing me with. When I recognize an issue, I find a way to cut out time by streamlining the process for the most accurate repetition, like any programmer would/should/could.
I noticed myself taking a lot of screenshots with compiz’ built in screenshot tool (I can hold a hotkey and drag a box to take newspaper style clippings). This is very fast and simple, I highly recommend enabling this option and getting used to it. However, I don’t care much for clicking around on clunky websites to upload images.
This is where my adventure starts… when I find out a very useful feature was deprecated and not replaced in Ubuntu. I don’t use the new notification area, it has too much I don’t need, I never liked the behavior of these new osd-notify notifications which I found out now are even more worthless (sorry team).
Using Sane Notifications in Ubuntu
After reinstalling the GNOME default notifications system in Ubuntu I was able to use SANE notifications that actually… KICK ASS! I don’t understand how osd-notify is better than these which even comes with it’s own notification properties panel. Maybe it’s an under the hood thing…
Regardless, there is no doubt in my mind that notifications that you hover, can still slightly see but click through but cannot perform any actions, even a close, are just plan stupid and annoying..

Screenshot of the script I ended up writing using the “better” notification system to ask me if I want to upload the screenshot I just took to the server.
Linking the notifications
I linked the notifications the following way:

Python Script for Notification that Prompts for File Upload to Server
Knowing diddly squat about Python, I chugged forward with my classic notification popups that allow for interaction as it had the most activity around it and some examples available in /usr/share/doc/python-notify/examples/
I ended coming up with the following script thanks to some help from a few people in #python on irc.freenode.org
This is outside of the scope of this blogpost but this script assumes you have setup passwordless ssh to your server.
#!/usr/bin/env python # # Title: Notification 'scp' Option # Author: Tyler Mulligan (tyler@detrition.net) # Date: 06/25/2010 # Description: # Used in combination with an event, such as an action or cron # the latest file from a folder will be scped to your server and will copy # the http location of the that file to your clipboard # # Orginally developed to be piped to from compiz screenshot tool # # # The MIT License # # Copyright (c) 2010 Tyler Mulligan # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE # import pygtk pygtk.require('2.0') import gtk import pynotify import sys import os import subprocess user = "user" host = "server.com" lfolder = "/home/user/screenshots/" hfolder = "/home/remote_user/screenshots/" httplink = "http://"+host+"/screenshots/" timeout = 5000 # Set position display = gtk.gdk.display_get_default() screen = display.get_default_screen() x = screen.get_width() - 1 y = screen.get_height() - 1 #x = 1919 #y = 12 def upload_cb(n, action): assert action == "upload" start = os.path.abspath(lfolder) f = max([(os.path.getmtime(os.path.join(start,x)),x) for x in os.listdir(start)])[1] # setup URL in clipboard clipboard = gtk.clipboard_get() clipboard.set_text(httplink + f) # make our data available to other applications clipboard.store() os.system('scp "%s" "%s@%s:%s"' % (lfolder + f, user, host, hfolder) ).wait() n.close() gtk.main_quit() def ignore_cb(n, action): assert action == "ignore" n.close() gtk.main_quit() if __name__ == '__main__': if not pynotify.init("Notifier 'scp' Option"): sys.exit(1) # Setup Popup n = pynotify.Notification("Upload to Server?") n.set_urgency(pynotify.URGENCY_NORMAL) n.set_timeout(timeout) n.set_category("device") n.add_action("upload", "Yes, Upload", upload_cb) n.add_action("ignore", "Ignore", ignore_cb) # Set position n.set_hint("x", x) n.set_hint("y", y) if not n.show(): print "Failed to send notification" sys.exit(1) gtk.main()
edit: I updated the script with some position information — I love that I can put the notifications ANYWHERE on my screen. I also variablized the timeout.
edit 2: Here is a video of my improved script in action — I’ll post the source later
http://interwebninja.com/videos/compiz-screenshot-piped-to-notification-daemon-for-upload.ogv
Going Beyond
The Compiz example is just something that server my immediate needs. The possibilities are however endless. You can for example, link this script to a cron job asking you if you want to sync some other sort of file. Or perhaps you’d like to add multiple buttons to give yourself a folder / server choice.
Happy hacking