1. michael b
  2. fsgamer

Source

fsgamer / fsgamer / fsgamer_app.py

import xdg.Menu
import xdg.DesktopEntry as d
import xdg.BaseDirectory
import json
import os
import time
import subprocess

import optparse
import signal

import gettext
from gettext import gettext as _
gettext.textdomain('fsgamer')
import sys
import re

import utils

DEFAULT_ICON = 'gtk-file'
DESKTOP_TEMPLATE = """
    [Desktop Entry]
    Comment=%(comment)s
    Name=%(name)s
    Exec=%(exec)s
    Terminal=false
    Version=
    Type=Application
    Categories=Game;
    Icon=%(icon)s
""".replace("    ", "").strip()

def _load_de(fn):
    try:
        new_entry = d.DesktopEntry(filename=fn)
    except:
        # Catch all exception just to keep things rolling incase one of
        # these is invalid in a way I cannot even imagine
        print("Uh oh, could not load ", fn)
    return new_entry

def get_xdg_applications():
    lst = []

    def add_menu_to_list(menu, depth = 0):
        for entry in menu.getEntries():
            if isinstance(entry, xdg.Menu.Menu):
                add_menu_to_list(entry, depth)
            elif isinstance(entry, xdg.Menu.MenuEntry):
                lst.append((menu.getPath(), entry.DesktopFileID, entry.DesktopEntry.getFileName()))
    add_menu_to_list(xdg.Menu.parse())

    desktop_entries = []
    for menu_path, fileid, fn in lst:
        if fn.endswith(".fsgamer.desktop"):
            # One of our own, skip
            continue

        new_entry = _load_de(fn)
        if new_entry.getName().lower().strip() == 'fsgamer':
            # It's probably me!
            continue

        new_entry.filename = fn
        desktop_entries.append(new_entry)
    return desktop_entries

class DesktopEntryList(list):
    def __init__(self, initial=None):
        entries = initial or get_xdg_applications()
        list.__init__(self, entries)

    def filter_games(self):
        def game_filter(item):
            l = map(str.lower, item.getCategories())
            return any(map(lambda s: 'game' in s,  l))
        return DesktopEntryList(filter(game_filter, self))

    def __repr__(self):
        s = u''
        for item in self:
            s += u'%s - Icon(%s) - Categories(%s)\n' % (repr(item.getName()), repr(item.getIcon()), repr(item.getCategories()))
        return s


def _config_path():
    path = os.path.join(xdg.BaseDirectory.xdg_config_home, 'fsgamer')
    if not os.path.exists(path):
        os.makedirs(path)
    return os.path.abspath(path)

def _local_share_applications_path():
    p = os.path.join(utils.HOMEDIR, ".local", "share", "applications")
    if not os.path.exists(p):
        os.makedirs(p)
    return os.path.abspath(p)

class ApplicationList(list):
    def __init__(self, path=None):
        # Defaults to home directory
        list.__init__([])

        if not path:
            path = _config_path()

        for dirpath, dirnames, filenames in os.walk(path):
            for filename in filenames:
                if filename.endswith('.desktop'):
                    path = os.path.join(dirpath, filename)
                    app = Application(desktop=_load_de(path), filepath=path)
                    self.append(app)

TITLE_TRANS = ''.join(chr(c) if chr(c).isupper() or chr(c).islower() else '_' for c in range(256))

def _to_filename(s):
    return s.translate(TITLE_TRANS).strip('_')


class Application(object):
    def __init__(self, desktop=None, command=None, filepath=None, options={}):
        self.filepath = filepath
        self.desktop = desktop
        self.command = command
        self.opts = {}

        # If the application is running, this is set to the Popen of the xinit
        # of the new xsession
        self.process = None

        if self.desktop:
            e = self.desktop.getExec().strip()
            if e.startswith(utils.BINPATH):
                # Strip away our options for its internal representation
                options, args = utils.parse_options(e)
                self.desktop.set('Exec', options.exe)
                self.opts = options.opts

    def get_xdg_desktop(self):
        if self.desktop:
            return self.desktop

        command = self.command

        # Allow ~
        if command.startswith('~'):
            command = os.path.expanduser(command)

        # If its a path...
        if os.path.exists(command):
            command = os.path.realpath(command)

        # Otherwise, it is a command
        s = DESKTOP_TEMPLATE % {
            'comment': self.command,
            'name': self.get_name(),
            'exec': command,
            'icon': DEFAULT_ICON,
        }

        tmp_path = utils.write_tmp_file('desktop/%s' % self.get_filename(), s)
        return d.DesktopEntry(tmp_path)

    @staticmethod
    def from_cli(options, args):
        opts = {}

        if options.opts:
            opt = options.opts

        if options.desktop:
            a = Application.from_filepath(options.desktop, opts)
        else:
            a = Application.from_command(options.exe, opts)

        return a

    @staticmethod
    def from_filepath(filepath, opts={}):
        desktop = d.DesktopEntry(filepath)
        a = Application(desktop=desktop, filepath=filepath)
        a.opts.update(opts)
        return a

    @staticmethod
    def from_command(command, opts={}):
        a = Application(command=command)
        a.opts.update(opts)
        return a


    def get_name(self):
        if self.command:
            # figure out
            name = self.command
            try: name = name.split(' ')[0].split('/')[-1]
            except: pass
            name = name.capitalize()
            name = name.replace('_', ' ')
        else:
            name = self.desktop.getName()
        return name


    def write_desktop(self, path=None, name_suffix=""):
        old_name = self.get_name()

        # Get or create a xdg desktop object
        desktop = self.get_xdg_desktop()
        if name_suffix:
            desktop.set('Name', desktop.getName() + name_suffix)

        if not path:
            base_path = _config_path()
            filename = self.get_filename()
            path = os.path.join(base_path, filename)

        # Set exec
        old_exec = desktop.getExec()
        exe = self.build_exec()
        desktop.set('Exec', exe)

        # Use xdg's built in writer
        desktop.write(path)

        # Reset properties that we overwrote
        desktop.set('Name', old_name)
        desktop.set('Exec', old_exec)

    def build_exec(self):
        d = {
            'opts': self.opts,
            'exec': self.get_exec(),
        }

        if self.filepath:
            d['desktop'] = self.filepath

        return utils.unparse_options(d)

    def get_exec(self):
        if self.command:
            return self.command
        else:
            return self.desktop.getExec()

    def get_pixbuf(self, icon_theme, size=64):
        if self.desktop:
            try:
                pixbuf = icon_theme.load_icon(self.desktop.getIcon(), size, 0)
            except:
                pixbuf = icon_theme.load_icon(DEFAULT_ICON, size, 0)
        else:
            pixbuf = icon_theme.load_icon(DEFAULT_ICON, size, 0)
        return pixbuf

    def run_outer(self, replace=True):
        """
        Runs application, with wrapper (ie, to display GUI on this desktop, and
        then run_inner on another xsession)
        """
        e = self.build_exec()

        # Run outer:
        self.process = utils.shell_run(e, replace=True)

    def get_exec_opts(self):
        if self.command:
            e = self.command
        else:
            e = self.desktop.getExec()
        if self.opts.get('xserer_script', '').strip():
            return "%s &\n%s" % (e, self.opts['xserver_script'].strip())
        else:
            return e

    def get_xinit_fsgwm(self):
        if self.command:
            e = self.command
        else:
            e = self.desktop.getExec()

        if self.opts.get('xserver_script', '').strip():
            inner_script = "%s &\n%s" % (e, self.opts['xserver_script'].strip())

            inner_script_sh = utils.shell_run.sh_from_script(inner_script)
            script = utils.shellbuilder.xinit_fsgwm(inner_script_sh, self.opts)
        else:
            script = utils.shellbuilder.xinit_fsgwm(e, self.opts)

        return script


    def run(self):
        """
        Runs application on the other desktop (without GUI)
        """
        if self.process:
            return

        script = self.get_xinit_fsgwm()

        # This is the key function that actually runs this command in a
        # separate xsession
        self.process = utils.shell_run(script)

    def kill(self):
        """
        Kills application if running
        """
        if not self.process:
            print "How do you kill that which is already dead?"
            return
        utils.shell_run.killall()
        #self.process.kill()
        #time.sleep(5)
        #os.killpg(0, signal.SIGKILL) # kill all processes in my group
        #self.process.wait()
        self.process = None

    def delete(self):
        if self.filepath:
            os.remove(self.filepath)
        self.remove_menu_icon()

    def get_filename(self):
        if self.filepath and os.path.basename(self.filepath):
            base_filename = os.path.splitext(os.path.basename(self.filepath))[0]
        else:
            base_filename = re.sub(r'\W+', '_', self.get_name().lower()).strip('_')
        base_filename = base_filename.replace('.fsgamer', '')
        return "%s.fsgamer.desktop" % base_filename

    def get_menu_icon_path(self):
        """
        Gets the path for where this application would go if it were a menu
        icon
        """
        base_path = _local_share_applications_path()
        filename = self.get_filename()
        path = os.path.join(base_path, filename)
        return path

    def menu_icon_exists(self):
        path = self.get_menu_icon_path()
        return os.path.exists(path)

    def write_menu_icon(self):
        d_path = self.get_menu_icon_path()
        self.write_desktop(d_path, " - FSGamer")

    def remove_menu_icon(self):
        path = self.get_menu_icon_path()
        if os.path.exists(path):
            os.remove(path)

    def toggle_menu_icon(self):
        path = self.get_menu_icon_path()
        if os.path.exists(path):
            os.remove(path)
        else:
            self.write_menu_icon()


def main():
    a = ApplicationList()

if __name__ == "__main__":
    main()