# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
from os.path import join, splitext, basename
from utool import util_inject
print, rrr, profile = util_inject.inject2(__name__)
[docs]def add_new_mimetype_association(ext, mime_name, exe_fpath=None, dry=True):
"""
TODO: move to external manager and generalize
Args:
ext (str): extension to associate
mime_name (str): the name of the mime_name to create (defaults to ext)
exe_fpath (str): executable location if this is for one specific file
References:
https://wiki.archlinux.org/index.php/Default_applications#Custom_file_associations
Args:
ext (str): extension to associate
exe_fpath (str): executable location
mime_name (str): the name of the mime_name to create (defaults to ext)
CommandLine:
python -m utool.util_ubuntu --exec-add_new_mimetype_association
# Add ability to open ipython notebooks via double click
python -m utool.util_ubuntu --exec-add_new_mimetype_association --mime-name=ipynb+json --ext=.ipynb --exe-fpath=/usr/local/bin/ipynb
python -m utool.util_ubuntu --exec-add_new_mimetype_association --mime-name=ipynb+json --ext=.ipynb --exe-fpath=jupyter-notebook --force
python -m utool.util_ubuntu --exec-add_new_mimetype_association --mime-name=sqlite --ext=.sqlite --exe-fpath=sqlitebrowser
Example:
>>> # DISABLE_DOCTEST
>>> # SCRIPT
>>> from utool.util_ubuntu import * # NOQA
>>> import utool as ut
>>> ext = ut.get_argval('--ext', type_=str, default=None)
>>> mime_name = ut.get_argval('--mime_name', type_=str, default=None)
>>> exe_fpath = ut.get_argval('--exe_fpath', type_=str, default=None)
>>> dry = not ut.get_argflag('--force')
>>> result = add_new_mimetype_association(ext, mime_name, exe_fpath, dry)
>>> print(result)
"""
import utool as ut
terminal = True
mime_codeblock = ut.codeblock(
"""
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-{mime_name}">
<glob-deleteall/>
<glob pattern="*{ext}"/>
</mime-type>
</mime-info>
"""
).format(**locals())
prefix = ut.truepath('~/.local/share')
mime_dpath = join(prefix, 'mime/packages')
mime_fpath = join(mime_dpath, 'application-x-{mime_name}.xml'.format(**locals()))
print(mime_codeblock)
print('---')
print(mime_fpath)
print('L___')
if exe_fpath is not None:
exe_fname_noext = splitext(basename(exe_fpath))[0]
app_name = exe_fname_noext.replace('_', '-')
nice_name = ' '.join(
[
word[0].upper() + word[1:].lower()
for word in app_name.replace('-', ' ').split(' ')
]
)
app_codeblock = ut.codeblock(
"""
[Desktop Entry]
Name={nice_name}
Exec={exe_fpath}
MimeType=application/x-{mime_name}
Terminal={terminal}
Type=Application
Categories=Utility;Application;
Comment=Custom App
"""
).format(**locals())
app_dpath = join(prefix, 'applications')
app_fpath = join(app_dpath, '{app_name}.desktop'.format(**locals()))
print(app_codeblock)
print('---')
print(app_fpath)
print('L___')
# WRITE FILES
if not dry:
ut.ensuredir(mime_dpath)
ut.ensuredir(app_dpath)
ut.writeto(mime_fpath, mime_codeblock, verbose=ut.NOT_QUIET, n=None)
if exe_fpath is not None:
ut.writeto(app_fpath, app_codeblock, verbose=ut.NOT_QUIET, n=None)
# UPDATE BACKENDS
# ut.cmd('update-mime-database /usr/share/mime')
# ~/.local/share/applications/mimeapps.list
print(
ut.codeblock(
"""
Run these commands:
update-desktop-database ~/.local/share/applications
update-mime-database ~/.local/share/mime
"""
)
)
if exe_fpath is not None:
ut.cmd('update-desktop-database ~/.local/share/applications')
ut.cmd('update-mime-database ~/.local/share/mime')
else:
print('dry_run')
[docs]def make_application_icon(exe_fpath, dry=True, props={}):
r"""
CommandLine:
python -m utool.util_ubuntu --exec-make_application_icon --exe=cockatrice --icon=/home/joncrall/code/Cockatrice/cockatrice/resources/cockatrice.png
python -m utool.util_ubuntu --exec-make_application_icon --exe=cockatrice --icon=/home/joncrall/code/Cockatrice/cockatrice/resources/cockatrice.png
python -m utool.util_ubuntu --exec-make_application_icon --exe=/opt/zotero/zotero --icon=/opt/zotero/chrome/icons/default/main-window.ico
python -m utool.util_ubuntu --exec-make_application_icon --exe "env WINEPREFIX="/home/joncrall/.wine" wine C:\\\\windows\\\\command\\\\start.exe /Unix /home/joncrall/.wine32-dotnet45/dosdevices/c:/users/Public/Desktop/Hearthstone.lnk" --path "/home/joncrall/.wine/dosdevices/c:/Program Files (x86)/Hearthstone"
# Exec=env WINEPREFIX="/home/joncrall/.wine" wine /home/joncrall/.wine/drive_c/Program\ Files\ \(x86\)/Battle.net/Battle.net.exe
--icon=/opt/zotero/chrome/icons/default/main-window.ico
python -m utool.util_ubuntu --exec-make_application_icon --exe=/home/joncrall/code/build-ArenaTracker-Desktop_Qt_5_6_1_GCC_64bit-Debug
update-desktop-database ~/.local/share/applications
Example:
>>> # DISABLE_DOCTEST
>>> from utool.util_ubuntu import * # NOQA
>>> import utool as ut
>>> exe_fpath = ut.get_argval('--exe', default='cockatrice')
>>> icon = ut.get_argval('--icon', default=None)
>>> dry = not ut.get_argflag(('--write', '-w'))
>>> props = {'terminal': False, 'icon': icon}
>>> result = make_application_icon(exe_fpath, dry, props)
>>> print(result)
"""
import utool as ut
exe_fname_noext = splitext(basename(exe_fpath))[0]
app_name = exe_fname_noext.replace('_', '-')
nice_name = ' '.join(
[
word[0].upper() + word[1:].lower()
for word in app_name.replace('-', ' ').split(' ')
]
)
lines = [
'[Desktop Entry]',
'Name={nice_name}',
'Exec={exe_fpath}',
]
if 'mime_name' in props:
lines += ['MimeType=application/x-{mime_name}']
if 'icon' in props:
lines += ['Icon={icon}']
if props.get('path'):
lines += ['Path={path}']
# if props.get('comment'):
# lines += ['Path={comment}']
lines += [
'Terminal={terminal}',
'Type=Application',
'Categories=Utility;Application;',
'Comment=Custom App',
]
fmtdict = locals()
fmtdict.update(props)
prefix = ut.truepath('~/.local/share')
app_codeblock = '\n'.join(lines).format(**fmtdict)
app_dpath = join(prefix, 'applications')
app_fpath = join(app_dpath, '{app_name}.desktop'.format(**locals()))
print(app_codeblock)
print('---')
print(app_fpath)
print('L___')
if not dry:
ut.writeto(app_fpath, app_codeblock, verbose=ut.NOT_QUIET, n=None)
ut.cmd('update-desktop-database ~/.local/share/applications')
[docs]class XCtrl(object):
r"""
xdotool key ctrl+shift+i
References:
http://superuser.com/questions/382616/detecting-currently-active-window
http://askubuntu.com/questions/455762/xbindkeys-wont-work-properly
Ignore:
xdotool keyup --window 0 7 type --clearmodifiers ---window 0 '%paste'
# List current windows:
wmctrl -l
# Get current window
xdotool getwindowfocus getwindowname
#====
# Get last opened window
#====
win_title=x-terminal-emulator.X-terminal-emulator
key_ = 'x-terminal-emulator.X-terminal-emulator'
# Get all windows in current workspace
workspace_number=`wmctrl -d | grep '\*' | cut -d' ' -f 1`
win_list=`wmctrl -lx | grep $win_title | grep " $workspace_number " | awk '{print $1}'`
# Get stacking order of windows in current workspace
win_order=$(xprop -root|grep "^_NET_CLIENT_LIST_STACKING" | tr "," " ")
echo $win_order
CommandLine:
python -m utool.util_ubuntu XCtrl
Example:
>>> # DISABLE_DOCTEST
>>> # Script
>>> import utool as ut
>>> from utool import util_ubuntu
>>> orig_window = []
>>> ut.copy_text_to_clipboard(ut.lorium_ipsum())
>>> doscript = [
>>> ('focus', 'x-terminal-emulator.X-terminal-emulator'),
>>> ('type', '%paste'),
>>> ('key', 'KP_Enter'),
>>> # ('focus', 'GVIM')
>>> ]
>>> util_ubuntu.XCtrl.do(*doscript, sleeptime=.01)
Ignore:
>>> ut.copy_text_to_clipboard(text)
>>> if '\n' in text or len(text) > 20:
>>> text = '\'%paste\''
>>> else:
>>> import pipes
>>> text = pipes.quote(text.lstrip(' '))
>>> ('focus', 'GVIM'),
>>> #
>>> doscript = [
>>> ('focus', 'x-terminal-emulator.X-terminal-emulator'),
>>> ('type', text),
>>> ('key', 'KP_Enter'),
>>> ]
>>> ut.util_ubuntu.XCtrl.do(*doscript, sleeptime=.01)
"""
# @staticmethod
# def send_raw_key_input(keys):
# import utool as ut
# print('send key input: %r' % (keys,))
# args = ['xdotool', 'type', keys]
# ut.cmd(*args, quiet=True, silence=True)
[docs] @staticmethod
def move_window(win_key, bbox):
"""
CommandLine:
# List windows
wmctrl -l
# List desktops
wmctrl -d
# Window info
xwininfo -id 60817412
python -m utool.util_ubuntu XCtrl.move_window joncrall 0+1920,680,400,600,400
python -m utool.util_ubuntu XCtrl.move_window joncrall [0,0,1000,1000]
python -m utool.util_ubuntu XCtrl.move_window GVIM special2
python -m utool.util_ubuntu XCtrl.move_window joncrall special2
python -m utool.util_ubuntu XCtrl.move_window x-terminal-emulator.X-terminal-emulator [0,0,1000,1000]
# >>> import utool as ut
# >>> from utool import util_ubuntu
# >>> orig_window = []
# >>> X = util_ubuntu.XCtrl
win_key = 'x-terminal-emulator.X-terminal-emulator'
win_id = X.findall_window_ids(key)[0]
python -m utool.util_ubuntu XCtrl.findall_window_ids gvim --src
"""
import utool as ut
try:
import wbia.plottool.screeninfo as screeninfo
except ImportError:
import wbia.plottool.screeninfo as screeninfo
monitor_infos = {i + 1: screeninfo.get_resolution_info(i) for i in range(2)}
# TODO: cut out borders
# TODO: fix screeninfo monitor offsets
# TODO: dynamic num screens
def rel_to_abs_bbox(m, x, y, w, h):
""" monitor_num, relative x, y, w, h """
minfo = monitor_infos[m]
# print('minfo(%d) = %s' % (m, ut.repr3(minfo),))
mx, my = minfo['off_x'], minfo['off_y']
mw, mh = minfo['pixels_w'], minfo['pixels_h']
# Transform to the absolution position
abs_x = (x * mw) + mx
abs_y = (y * mh) + my
abs_w = w * mw
abs_h = h * mh
abs_bbox = [abs_x, abs_y, abs_w, abs_h]
abs_bbox = ','.join(map(str, map(int, abs_bbox)))
return abs_bbox
if win_key.startswith('joncrall') and bbox == 'special2':
# Specify the relative position
abs_bbox = rel_to_abs_bbox(m=2, x=0.0, y=0.7, w=1.0, h=0.3)
elif win_key.startswith('GVIM') and bbox == 'special2':
# Specify the relative position
abs_bbox = rel_to_abs_bbox(m=2, x=0.0, y=0.0, w=1.0, h=0.7)
else:
abs_bbox = ','.join(map(str, eval(bbox)))
print('MOVING: win_key = %r' % (win_key,))
print('TO: abs_bbox = %r' % (abs_bbox,))
# abs_bbox.replace('[', '').replace(']', '')
# get = lambda cmd: ut.cmd2(' '.join(["/bin/bash", "-c", cmd]))['out'] # NOQA
win_id = XCtrl.find_window_id(win_key, error='raise')
print('MOVING: win_id = %r' % (win_id,))
fmtdict = locals()
cmd_list = [
('wmctrl -ir {win_id} -b remove,maximized_horz'.format(**fmtdict)),
('wmctrl -ir {win_id} -b remove,maximized_vert'.format(**fmtdict)),
('wmctrl -ir {win_id} -e 0,{abs_bbox}'.format(**fmtdict)),
]
print('\n'.join(cmd_list))
for cmd in cmd_list:
ut.cmd2(cmd)
[docs] @staticmethod
def findall_window_ids(pattern):
"""
CommandLine:
wmctrl -l
python -m utool.util_ubuntu XCtrl.findall_window_ids gvim --src
python -m utool.util_ubuntu XCtrl.findall_window_ids gvim --src
python -m utool.util_ubuntu XCtrl.findall_window_ids joncrall --src
xprop -id
wmctrl -l | awk '{print $1}' | xprop -id
0x00a00007 | grep "WM_CLASS(STRING)"
"""
import utool as ut
cmdkw = dict(verbose=False, quiet=True, silence=True)
command = "wmctrl -lx | grep '%s' | awk '{print $1}'" % (pattern,)
# print(command)
winid_list = ut.cmd(command, **cmdkw)[0].strip().split('\n')
winid_list = [h for h in winid_list if h]
winid_list = [int(h, 16) for h in winid_list]
return winid_list
[docs] @staticmethod
def sort_window_ids(winid_list, order='mru'):
"""
Orders window ids by most recently used
"""
import utool as ut
winid_order = XCtrl.sorted_window_ids(order)
sorted_win_ids = ut.isect(winid_order, winid_list)
return sorted_win_ids
[docs] @staticmethod
def killold(pattern, num=4):
"""
Leaves no more than `num` instances of a program alive. Ordering is
determined by most recent usage.
CommandLine:
python -m utool.util_ubuntu XCtrl.killold gvim 2
>>> import utool as ut
>>> from utool import util_ubuntu
>>> XCtrl = util_ubuntu.XCtrl
>>> pattern = 'gvim'
>>> num = 2
"""
import utool as ut
cmdkw = dict(verbose=False, quiet=True, silence=True)
num = int(num)
winid_list = XCtrl.findall_window_ids(pattern)
winid_list = XCtrl.sort_window_ids(winid_list, 'mru')[num:]
output_lines = (
ut.cmd("""wmctrl -lxp | awk '{print $1 " " $3}'""", **cmdkw)[0]
.strip()
.split('\n')
)
output_fields = [line.split(' ') for line in output_lines]
output_fields = [(int(wid, 16), int(pid)) for wid, pid in output_fields]
pid_list = [pid for wid, pid in output_fields if wid in winid_list]
import psutil
for pid in pid_list:
proc = psutil.Process(pid=pid)
proc.kill()
[docs] @staticmethod
def sorted_window_ids(order='mru'):
"""
Returns window ids orderd by criteria
default is mru (most recently used)
CommandLine:
xprop -root | grep "^_NET_CLIENT_LIST_STACKING" | tr "," " "
python -m utool.util_ubuntu XCtrl.sorted_window_ids
"""
import utool as ut
if order in ['mru', 'lru']:
cmdkw = dict(verbose=False, quiet=True, silence=True)
winid_order_str = ut.cmd(
'xprop -root | grep "^_NET_CLIENT_LIST_STACKING"', **cmdkw
)[0]
winid_order = winid_order_str.split('#')[1].strip().split(', ')[::-1]
winid_order = [int(h, 16) for h in winid_order]
if order == 'lru':
winid_order = winid_order[::-1]
else:
raise NotImplementedError(order)
return winid_order
[docs] @staticmethod
def find_window_id(pattern, method='mru', error='raise'):
"""
xprop -id 0x00a00007 | grep "WM_CLASS(STRING)"
"""
import utool as ut
winid_candidates = XCtrl.findall_window_ids(pattern)
if len(winid_candidates) == 0:
if error == 'raise':
available_windows = ut.cmd2('wmctrl -l')['out']
msg = 'No window matches pattern=%r' % (pattern,)
msg += '\navailable windows are:\n%s' % (available_windows,)
print(msg)
raise Exception(msg)
win_id = None
elif len(winid_candidates) == 1:
win_id = winid_candidates[0]
else:
# print('Multiple (%d) windows matches pattern=%r' % (
# len(winid_list), pattern,))
# Find most recently used window with the focus name.
win_id = XCtrl.sort_window_ids(winid_candidates, method)[0]
return win_id
[docs] @staticmethod
def current_gvim_edit(op='e', fpath=''):
r"""
CommandLine:
python -m utool.util_ubuntu XCtrl.current_gvim_edit sp ~/.bashrc
"""
import utool as ut
fpath = ut.unexpanduser(ut.truepath(fpath))
# print('fpath = %r' % (fpath,))
ut.copy_text_to_clipboard(fpath)
# print(ut.get_clipboard())
doscript = [
('focus', 'gvim'),
('key', 'Escape'),
('type2', ';' + op + ' ' + fpath),
# ('type2', ';' + op + ' '),
# ('key', 'ctrl+v'),
('key', 'KP_Enter'),
]
XCtrl.do(*doscript, verbose=0, sleeptime=0.001)
[docs] @staticmethod
def copy_gvim_to_terminal_script(text, return_to_win='1', verbose=0, sleeptime=0.02):
"""
import utool.util_ubuntu
utool.util_ubuntu.XCtrl.copy_gvim_to_terminal_script('print("hi")', verbose=1)
python -m utool.util_ubuntu XCtrl.copy_gvim_to_terminal_script "echo hi" 1 1
If this doesn't work make sure pyperclip is installed and set to xsel
print('foobar')
echo hi
"""
# Prepare to send text to xdotool
import utool as ut
import utool.util_ubuntu
ut.copy_text_to_clipboard(text)
if verbose:
print('text = %r' % (text,))
print(ut.get_clipboard())
import re
terminal_pattern = r'\|'.join(
[
'terminal',
re.escape('terminator.Terminator'), # gtk3 terminator
re.escape('x-terminal-emulator.X-terminal-emulator'), # gtk2 terminator
]
)
# Build xdtool script
doscript = [
('remember_window_id', 'ACTIVE_WIN'),
# ('focus', 'x-terminal-emulator.X-terminal-emulator'),
('focus', terminal_pattern),
('key', 'ctrl+shift+v'),
('key', 'KP_Enter'),
]
if '\n' in text:
# Press enter twice for multiline texts
doscript += [
('key', 'KP_Enter'),
]
if return_to_win == '1':
doscript += [
('focus_id', '$ACTIVE_WIN'),
]
# execute script
# verbose = 1
utool.util_ubuntu.XCtrl.do(*doscript, sleeptime=sleeptime, verbose=verbose)
[docs] @staticmethod
def do(*cmd_list, **kwargs):
import utool as ut
import time
import six
import sys
verbose = kwargs.get('verbose', False)
orig_print = globals()['print']
print = ut.partial(orig_print, file=kwargs.get('file', sys.stdout))
# print('Running xctrl.do script')
if verbose:
print('Executing x do: %s' % (ut.repr4(cmd_list),))
debug = False
cmdkw = dict(verbose=False, quiet=True, silence=True)
# http://askubuntu.com/questions/455762/xbindkeys-wont-work-properly
# Make things work even if other keys are pressed
defaultsleep = 0.0
sleeptime = kwargs.get('sleeptime', defaultsleep)
time.sleep(0.05)
out, err, ret = ut.cmd('xset r off', **cmdkw)
if debug:
print('----------')
print('xset r off')
print('ret = %r' % (ret,))
print('err = %r' % (err,))
print('out = %r' % (out,))
memory = {}
tmpverbose = 0
for count, item in enumerate(cmd_list):
# print('item = %r' % (item,))
sleeptime = kwargs.get('sleeptime', defaultsleep)
if tmpverbose:
print('moving on')
tmpverbose = 0
nocommand = 0
assert isinstance(item, tuple)
assert len(item) >= 2
xcmd, key_ = item[0:2]
if len(item) >= 3:
if isinstance(item[2], six.string_types) and item[2].endswith('?'):
sleeptime = float(item[2][:-1])
tmpverbose = 1
print('special command sleep')
print('sleeptime = %r' % (sleeptime,))
else:
sleeptime = float(item[2])
if xcmd == 'focus':
key_ = str(key_)
if key_.startswith('$'):
key_ = memory[key_[1:]]
pattern = key_
win_id = XCtrl.find_window_id(pattern, method='mru')
if win_id is None:
args = ['wmctrl', '-xa', pattern]
else:
args = ['wmctrl', '-ia', hex(win_id)]
elif xcmd == 'focus_id':
key_ = str(key_)
if key_.startswith('$'):
key_ = memory[key_[1:]]
args = ['wmctrl', '-ia', hex(key_)]
elif xcmd == 'remember_window_id':
out, err, ret = ut.cmd('xdotool getwindowfocus', **cmdkw)
memory[key_] = int(out.strip())
nocommand = True
args = []
elif xcmd == 'remember_window_name':
out, err, ret = ut.cmd('xdotool getwindowfocus getwindowname', **cmdkw)
import pipes
memory[key_] = pipes.quote(out.strip())
nocommand = True
args = []
elif xcmd == 'type':
args = [
'xdotool',
'keyup',
'--window',
'0',
'7',
'type',
'--clearmodifiers',
'--window',
'0',
str(key_),
]
elif xcmd == 'type2':
import pipes
args = ['xdotool', 'type', pipes.quote(str(key_))]
elif xcmd == 'xset-r-on':
args = ['xset', 'r', 'on']
elif xcmd == 'xset-r-off':
args = ['xset', 'r', 'off']
else:
args = ['xdotool', str(xcmd), str(key_)]
if verbose or tmpverbose:
print('\n\n# Step %d' % (count,))
print(args, ' '.join(args))
if nocommand:
continue
# print('args = %r -> %s' % (args, ' '.join(args),))
# print('args = %r' % (args,))
out, err, ret = ut.cmd(*args, **cmdkw)
if debug:
print('---- ' + xcmd + ' ------')
print(' '.join(args))
print('ret = %r' % (ret,))
print('err = %r' % (err,))
print('out = %r' % (out,))
if sleeptime > 0:
time.sleep(sleeptime)
out, err, ret = ut.cmd('xset r on', verbose=False, quiet=True, silence=True)
if debug:
print('----------')
print('xset r on')
print('ret = %r' % (ret,))
print('err = %r' % (err,))
print('out = %r' % (out,))
[docs] @staticmethod
def focus_window(winhandle, path=None, name=None, sleeptime=0.01):
"""
sudo apt-get install xautomation
apt-get install autokey-gtk
wmctrl -xa gnome-terminal.Gnome-terminal
wmctrl -xl
"""
import utool as ut
import time
print('focus: ' + winhandle)
args = ['wmctrl', '-xa', winhandle]
ut.cmd(*args, verbose=False, quiet=True)
time.sleep(sleeptime)
xctrl = XCtrl
[docs]def monitor_mouse():
"""
CommandLine:
python -m utool.util_ubuntu monitor_mouse
Example:
>>> # DISABLE_DOCTEST
>>> # SCRIPT
>>> from utool.util_ubuntu import * # NOQA
>>> import utool as ut
>>> monitor_mouse()
"""
import utool as ut
import re
import parse
mouse_ids = ut.cmd('xinput --list ', verbose=False, quiet=True)[0]
x = mouse_ids.decode('utf-8')
pattern = 'mouse'
pattern = 'trackball'
print(x)
grepres = ut.greplines(x.split('\n'), pattern, reflags=re.IGNORECASE)
mouse_id = parse.parse('{left}id={id}{right}', grepres[0][0])['id']
print('mouse_id = %r' % (mouse_id,))
import time
while True:
time.sleep(0.2)
out = ut.cmd('xinput --query-state ' + mouse_id, verbose=False, quiet=True)[0]
print(out)
if __name__ == '__main__':
"""
CommandLine:
python -m utool.util_ubuntu
python -m utool.util_ubuntu --allexamples
python -m utool.util_ubuntu --allexamples --noface --nosrc
"""
import multiprocessing
multiprocessing.freeze_support() # for win32
import utool as ut # NOQA
ut.doctest_funcs()