You are not logged in.
Pages: 1
As an exercise in learning python, i am trying to write an application launcher like dmenu. Only difference is that it will write everything directly on to screen (no window) using osdcat/pyosd. The problem is that it can launch other programs but launching urxvt fails.
os.spawnlp(os.P_NOWAIT,"urxvt")
launches urxvt but it is launched as a defunct process and no window appears
4812 0.0 0.0 0 0 pts/1 Z+ 20:52 0:00 [urxvt] <defunct>
when called with P_WAIT it returns with -11 which seems to be sigsegv
when called with os.system it works fine, but i guess its not the proper way to do this.
Any help will be appreciated.
PS: The partly baked code is below... It works with basic functionality ie you can launch programs other than urxvt. No editing of typed text, backspace,arrow keys etc currently. And it uses dmenu_path to get the list of programs
#!/usr/bin/python
from Xlib.display import Display
from Xlib import X
from Xlib import XK
import pyosd
import re
import commands
import os
a = commands.getoutput("dmenu_path").split("\n")
matches = []
def matchpattern(pattern,lst):
return filter(lambda x: re.match(pattern.lower(),x) , lst)
def printmatches(matches,myosd):
map(lambda x: myosd.display(x,line= matches.index(x)) , matches)
def clearlines(myosd):
map(lambda x: myosd.display("",line=x),range(0,9))
def run_pgm(program):
print "running"
print program
pid = os.fork()
if not pid:
os.execlp(program)
def handle_event(aEvent,disp,myosd,input):
keycode = aEvent.detail
if aEvent.type == X.KeyPress:
keysym = disp.keycode_to_keysym(keycode,0)
if keysym == XK.XK_Escape:
disp.ungrab_keyboard(X.CurrentTime)
return None
elif keysym == XK.XK_Return:
disp.ungrab_keyboard(X.CurrentTime)
matches = matchpattern(input,a)
print matches
if len(matches) == 0:
run_pgm(input)
else:
run_pgm(matches[0])
return None
else:
input += disp.lookup_string(keysym)
clearlines(myosd)
myosd.display(input)
printmatches(matchpattern(input,a)[0:9],myosd)
return input
else:
return input
def main():
disp = Display()
root = disp.screen().root
input = ""
myosd=pyosd.osd("-*-courier-bold-r-*-*-24-*-75-*-*-*-*-*",colour="#00FF00",timeout=0,pos=0,align=1,lines=10)
myosd.display("Run: ")
root.change_attributes(event_mask = X.KeyPressMask)
root.grab_keyboard(1,X.GrabModeAsync, X.GrabModeAsync,X.CurrentTime)
while input != None:
event = root.display.next_event()
input = handle_event(event,disp,myosd,input)
print
if __name__ == '__main__':
main()
Offline
Very interesting, I wonder about this too. Could you try some of these:
def run_pgm(program):
print "running"
print program
pid = os.fork()
if not pid:
os.setsid()
os.close(0)
os.close(1)
os.close(2)
os.execlp(program,program) #it needs arg0 right?
And...
import subprocess
def run_pgm(program):
print "running"
print program
pid = os.fork()
if not pid:
os.setsid()
os.close(0)
os.close(1)
os.close(2)
p=subprocess.Popen(program, shell=True)
p.wait()
os._exit()
edit: typo ('=' -> '.')
Last edited by Procyon (2009-02-13 16:29:37)
Offline
i guess its not the proper way to do this.
Indeed, you should be using the subprocess module.
Offline
u_no_hu wrote:i guess its not the proper way to do this.
Indeed, you should be using the subprocess module.
Subprocess module says nothing about replacing os.exec* though.
Offline
What I mentioned above is working for me, except that mplayer is still giving me trouble. If you close stdin and stdout the mplayer window won't show up at all. Even with -noconsolecontrols.
I just tried the following, reading and writing to /dev/null instead:
pid=os.fork()
if pid == 0:
os.setsid()
readnull=open("/dev/null",'r')
writenull=open("/dev/null",'w')
p=Popen(cmd,shell=True,stdin=readnull,stdout=writenull,stderr=writenull)
p.wait()
readnull.close()
writenull.close()
os._exit(0)
I wonder what better practice is generally, closing stdin or piping /dev/null to stdin.
EDIT:
Actually, this seems to create zombie processes too.
EDIT2:
OK. Time to read up on Unix stuff: http://en.wikipedia.org/wiki/SIGCHLD
So add
import signal
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
to the start of your program, the zombies will go away.
BTW using exec* is nicer than Popen because the spawned program won't have a python parent. That makes it look a lot better in htop etc.
Last edited by Procyon (2009-02-14 14:59:09)
Offline
I am now trying this in pyfmii:
-No zombie processes
-Bash launches the program, this is good because you can background bash syntax and that is really nice for a launcher program
-Double fork, so the new process isn't attached to the launcher but to init, this looks better in htop/pstree etc
-execvp instead of Popen so the name of the parent isn't the launcher
def main():
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
pid=os.fork()
if pid == 0:
os.setsid()
pid=os.fork()
if pid > 1:
os._exit(0)
os.closerange(0,os.sysconf("SC_OPEN_MAX")) #close all open FDs
os.open("/dev/null", os.O_RDWR) #make /dev/null stdin
os.dup2(0,1) #and stdout
os.dup2(0,2) #and stderr
toexec=["bash","-c"]
toexec.append(cmd) #run command through bash
os.execvp("bash",toexec)
This site has been a great help: http://code.activestate.com/recipes/278731/
Last edited by Procyon (2009-02-14 15:16:43)
Offline
Pages: 1