"""Copyright (c) 2010-2012 David Rio Vierra
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."""
# -# Modified by D.C.-G. for translation purpose
# !# Tests for file chooser
"""
mcplatform.py
Platform-specific functions, folder paths, and the whole fixed/portable nonsense.
"""
import logging
log = logging.getLogger(__name__)
import directories
import os
from os.path import dirname, exists, join
import sys
import platform
enc = sys.getfilesystemencoding()
hasXlibDisplay = False
if sys.platform == "win32":
if platform.architecture()[0] == "32bit":
plat = "win32"
if platform.architecture()[0] == "64bit":
plat = "win-amd64"
sys.path.append(join(directories.getDataDir(), "pymclevel", "build", "lib." + plat + "-2.6").encode(enc))
elif sys.platform in ['linux2', 'darwin']:
try:
import Xlib.display
import Xlib.X
import Xlib.protocol
hasXlibDisplay = True
except ImportError:
hasXlibDisplay = None
os.environ["YAML_ROOT"] = join(directories.getDataDir(), "pymclevel").encode(enc)
from pygame import display
from albow import request_new_filename, request_old_filename
from albow.translate import _
from pymclevel import minecraftSaveFileDir, getMinecraftProfileDirectory, getSelectedProfile
from datetime import datetime
import re
import subprocess
try:
import pygtk
pygtk.require('2.0')
import gtk
if gtk.pygtk_version < (2, 3, 90):
raise ImportError
hasGtk = True
except ImportError:
hasGtk = False # Using old method as fallback
texturePacksDir = os.path.join(getMinecraftProfileDirectory(getSelectedProfile()), "texturepacks")
# Compatibility layer for filters:
filtersDir = directories.filtersDir
schematicsDir = directories.schematicsDir
# !# Disabling platform specific file chooser:
# !# Please, don't touch these two lines and the 'platChooser' stuff. -- D.C.-G.
# platChooser = sys.platform in ('linux2', 'darwin')
platChooser = sys.platform == 'darwin'
[docs]def dynamic_arguments(func_to_replace, askFile_func):
def wrapper(initialDir, displayName, fileFormat):
if isinstance(fileFormat, tuple):
return func_to_replace(initialDir, displayName, fileFormat)
else:
def old_askSaveSchematic(initialDir, displayName, fileFormat):
dt = datetime.now().strftime("%Y-%m-%d--%H-%M-%S")
return askFile_func(initialDir,
title=_('Save this schematic...'),
defaultName=displayName + "_" + dt + "." + fileFormat,
filetype=_('Minecraft Schematics (*.{0})\0*.{0}\0\0').format(fileFormat),
suffix=fileFormat,
)
return old_askSaveSchematic(initialDir, displayName, fileFormat)
return wrapper
[docs]def getTexturePacks():
try:
return os.listdir(texturePacksDir)
except:
return []
# for k,v in os.environ.iteritems():
# try:
# os.environ[k] = v.decode(sys.getfilesystemencoding())
# except:
# continue
if sys.platform == "win32":
try:
from win32 import win32gui
from win32 import win32api
from win32.lib import win32con
except ImportError:
import win32gui
import win32api
import win32con
try:
import win32com.client
from win32com.shell import shell, shellcon # @UnresolvedImport
except:
pass
try:
import pywintypes
except:
pass
if sys.platform == 'darwin':
cmd_name = "Cmd"
option_name = "Opt"
else:
cmd_name = "Ctrl"
option_name = "Alt"
[docs]def OSXVersionChecker(name, compare):
"""Rediculously complicated function to compare current System version to inputted version."""
if compare != 'gt' and compare != 'lt' and compare != 'eq' and compare != 'gteq' and compare != 'lteq':
print "Invalid version check {}".format(compare)
return False
if sys.platform == 'darwin':
try:
systemVersion = platform.mac_ver()[0].split('.')
if len(systemVersion) == 2:
systemVersion.append('0')
major, minor, patch = 10, 0, 0
if name.lower() == 'cheetah':
minor = 0
patch = 4
elif name.lower() == 'puma':
minor = 1
patch = 5
elif name.lower() == 'jaguar':
minor = 2
patch = 8
elif name.lower() == 'panther':
minor = 3
patch = 9
elif name.lower() == 'tiger':
minor = 4
patch = 11
elif name.lower() == 'snow_leopard':
minor = 5
patch = 8
elif name.lower() == 'snow_leopard':
minor = 6
patch = 8
elif name.lower() == 'lion':
minor = 7
patch = 5
elif name.lower() == 'mountain_lion':
minor = 8
patch = 5
elif name.lower() == 'mavericks':
minor = 9
patch = 5
elif name.lower() == 'yosemite':
minor = 10
patch = 0
else:
major = 0
if int(systemVersion[0]) > int(major):
ret_val = 1
elif int(systemVersion[0]) < int(major):
ret_val = -1
else:
if int(systemVersion[1]) > int(minor):
ret_val = 1
elif int(systemVersion[1]) < int(minor):
ret_val = -1
else:
if int(systemVersion[2]) > int(patch):
ret_val = 1
elif int(systemVersion[2]) < int(patch):
ret_val = -1
else:
ret_val = 0
if ret_val == 0 and (compare == 'eq' or compare == 'gteq' or compare == 'lteq'):
return True
elif ret_val == -1 and (compare == 'lt' or compare == 'lteq'):
return True
elif ret_val == 1 and (compare == 'gt' or compare == 'gteq'):
return True
except:
print "An error occured determining the system version"
return False
else:
return False
lastSchematicsDir = None
lastSaveDir = None
[docs]def buildFileTypes(filetypes):
result = ""
for key in filetypes[0]:
ftypes = []
result += key + " ("
for ftype in filetypes[0][key]:
ftypes.append("*." + ftype)
result += ",".join(ftypes) + ")\0"
result += ";".join(ftypes) + "\0"
return result + "\0"
[docs]def askOpenFile(title='Select a Minecraft level....', schematics=False, suffixes=None):
title = _(title)
global lastSchematicsDir, lastSaveDir
if not suffixes:
suffixes = ["mclevel", "dat", "mine", "mine.gz"]
suffixesChanged = False
else:
suffixesChanged = True
initialDir = lastSaveDir or minecraftSaveFileDir
if schematics:
initialDir = lastSchematicsDir or directories.schematicsDir
def _askOpen(_suffixes):
if schematics:
_suffixes.append("schematic")
_suffixes.append("schematic.gz")
_suffixes.append("zip")
_suffixes.append("inv")
_suffixes.append("nbt")
# BO support
_suffixes.append("bo2")
_suffixes.append("bo3")
_suffixes.sort()
if sys.platform == "win32": # !#
if suffixesChanged:
sendSuffixes = _suffixes
else:
sendSuffixes = None
return askOpenFileWin32(title, schematics, initialDir, sendSuffixes)
elif hasGtk and not platChooser: # !# #Linux (When GTK 2.4 or newer is installed)
return askOpenFileGtk(title, _suffixes, initialDir)
else:
log.debug("Calling internal file chooser.")
log.debug("'initialDir' is %s (%s)" % (repr(initialDir), type(initialDir)))
try:
iDir = initialDir.encode(enc)
except Exception, e:
iDir = initialDir
log.debug("Could not encode 'initialDir' %s" % repr(initialDir))
log.debug("Encode function returned: %s" % e)
return request_old_filename(suffixes=_suffixes, directory=iDir)
filename = _askOpen(suffixes)
if filename:
if schematics:
lastSchematicsDir = dirname(filename)
else:
lastSaveDir = dirname(filename)
return filename
[docs]def askOpenFileWin32(title, schematics, initialDir, suffixes=None):
try:
# if schematics:
if not suffixes:
f = (_('Levels and Schematics') + '\0*.mclevel;*.dat;*.mine;*.mine.gz;*.schematic;*.zip;*.schematic.gz;*.inv;*.nbt\0' +
'*.*\0*.*\0\0')
else:
f = "All\0"
for suffix in suffixes:
f += "*." + suffix + ";"
f += "\0*.*\0\0"
# else:
# f = ('Levels (*.mclevel, *.dat;*.mine;*.mine.gz;)\0' +
# '*.mclevel;*.dat;*.mine;*.mine.gz;*.zip;*.lvl\0' +
# '*.*\0*.*\0\0')
(filename, customfilter, flags) = win32gui.GetOpenFileNameW(
hwndOwner=display.get_wm_info()['window'],
InitialDir=initialDir,
Flags=(win32con.OFN_EXPLORER
| win32con.OFN_NOCHANGEDIR
| win32con.OFN_FILEMUSTEXIST
| win32con.OFN_LONGNAMES
# |win32con.OFN_EXTENSIONDIFFERENT
),
Title=title,
Filter=f,
)
except Exception:
# print "Open File: ", e
pass
else:
return filename
[docs]def askOpenFileGtk(title, suffixes, initialDir):
fls = []
def run_dlg():
chooser = gtk.FileChooserDialog(title,
None, gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
chooser.set_default_response(gtk.RESPONSE_OK)
chooser.set_current_folder(initialDir)
chooser.set_current_name("world") # For some reason the Windows isn't closing if this line ins missing or the parameter is ""
# Add custom Filter
file_filter = gtk.FileFilter()
file_filter.set_name(_("Levels and Schematics"))
for suffix in suffixes:
file_filter.add_pattern("*." + suffix)
chooser.add_filter(file_filter)
# Add "All files" Filter
file_filter = gtk.FileFilter()
file_filter.set_name("All files")
file_filter.add_pattern("*")
chooser.add_filter(file_filter)
response = chooser.run()
if response == gtk.RESPONSE_OK:
fls.append(chooser.get_filename())
else:
fls.append(None)
chooser.destroy()
gtk.main_quit()
gtk.idle_add(run_dlg)
gtk.main()
return fls[0]
[docs]def askSaveSchematic(initialDir, displayName, fileFormats):
fileFormat = buildFileTypes(fileFormats)
dt = datetime.now().strftime("%Y-%m-%d--%H-%M-%S")
return askSaveFile(initialDir,
title=_('Save this schematic...'),
defaultName=displayName + "_" + dt + "." + fileFormats[0][fileFormats[0].keys()[0]][0],
filetype=fileFormat,
suffix=fileFormat,
)
[docs]def askCreateWorld(initialDir):
defaultName = name = _("Untitled World")
i = 0
while exists(join(initialDir, name)):
i += 1
name = defaultName + " " + str(i)
return askSaveFile(initialDir,
title=_('Name this new world.'),
defaultName=name,
filetype=_('Minecraft World\0*.*\0\0'),
suffix="",
)
[docs]def askSaveFile(initialDir, title, defaultName, filetype, suffix):
if sys.platform == "win32": # !#
try:
(filename, customfilter, flags) = win32gui.GetSaveFileNameW(
hwndOwner=display.get_wm_info()['window'],
InitialDir=initialDir,
Flags=win32con.OFN_EXPLORER | win32con.OFN_NOCHANGEDIR | win32con.OFN_OVERWRITEPROMPT,
File=defaultName,
DefExt=suffix,
Title=title,
Filter=filetype,
)
except Exception, e:
print "Error getting file name: ", e
return
try:
filename = filename[:filename.index('\0')]
filename = filename.decode(sys.getfilesystemencoding())
except:
pass
else:
# Reformat the Windows stuff
if "\0" in suffix and suffix.count("*.") > 1:
_suffix = suffix.split("\0")[:-2]
else:
_suffix = suffix
if "\0" in filetype and filetype.count("*.") > 1:
_filetype = filetype.split("\0")[:-2]
else:
_filetype = filetype
if hasGtk and not platChooser: # !# #Linux (When GTK 2.4 or newer is installed)
fls = []
def run_dlg():
chooser = gtk.FileChooserDialog(title,
None, gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_SAVE, gtk.RESPONSE_OK))
chooser.set_default_response(gtk.RESPONSE_OK)
chooser.set_current_folder(initialDir)
chooser.set_current_name(defaultName)
# Add custom Filter
if type(_filetype) in (list, tuple):
for i, filet in enumerate(_filetype):
if i % 2 == 0:
file_filter = gtk.FileFilter()
file_filter.set_name(filet)
if ";" in _suffix[i + 1]:
for _suff in _suffix.split(";"):
if _suff:
file_filter.add_pattern(_suff)
else:
file_filter.add_pattern(_suffix[i + 1])
chooser.add_filter(file_filter)
else:
file_filter = gtk.FileFilter()
file_filter.set_name(filetype[:filetype.index("\0")])
if "\0" in suffix and suffix.count("*.") > 1:
__suffix = suffix.split("\0")[:-2]
else:
__suffix = suffix
if type(__suffix) in (list, tuple):
for suff in __suffix:
file_filter.add_pattern("*." + suff)
else:
file_filter.add_pattern("*." + __suffix)
chooser.add_filter(file_filter)
# Add "All files" Filter
file_filter = gtk.FileFilter()
file_filter.set_name("All files")
file_filter.add_pattern("*")
chooser.add_filter(file_filter)
response = chooser.run()
if response == gtk.RESPONSE_OK:
fls.append(chooser.get_filename())
else:
fls.append(None)
chooser.destroy()
gtk.main_quit()
gtk.idle_add(run_dlg)
gtk.main()
filename = fls[0]
else: # Fallback
log.debug("Calling internal file chooser.")
log.debug("'initialDir' is %s (%s)" % (repr(initialDir), type(initialDir)))
log.debug("'defaultName' is %s (%s)" % (repr(defaultName), type(defaultName)))
try:
iDir = initialDir.encode(enc)
except:
iDir = initialDir
log.debug("Could not encode 'initialDir' %s" % repr(initialDir))
log.debug("Encode function returned: %s" % e)
try:
dName = defaultName.encode(enc)
except:
dName = defaultName
log.debug("Could not encode 'defaultName' %s" % repr(defaultName))
log.debug("Encode function returned: %s" % e)
if type(_suffix) in (list, tuple):
sffxs = [a[1:] for a in _suffix[1::2]]
sffx = sffxs.pop(0)
sffxs.append('.*')
else:
sffx = _suffix
sffxs = []
filename = request_new_filename(prompt=title,
suffix=sffx,
extra_suffixes=sffxs,
directory=iDir,
filename=dName,
pathname=None)
return filename
askSaveSchematic = dynamic_arguments(askSaveSchematic, askSaveFile)
# Start Open Folder Dialogs
# TODO: Possibly get an OS X dialog
[docs]def askOpenFolderWin32(title, initialDir):
try:
desktop_pidl = shell.SHGetFolderLocation(0, shellcon.CSIDL_DESKTOP, 0, 0)
pidl, display_name, image_list = shell.SHBrowseForFolder (
win32gui.GetDesktopWindow(),
desktop_pidl,
"Choose a folder",
0,
None,
None
)
return shell.SHGetPathFromIDList(pidl)
except pywintypes.com_error as e:
if e.args[0] == -2147467259:
print "Invalid folder selected"
pass
[docs]def askOpenFolderGtk(title, initialDir):
if hasGtk:
fls = []
def run_dlg():
chooser = gtk.FileChooserDialog(title,
None, gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
chooser.set_default_response(gtk.RESPONSE_OK)
chooser.set_current_folder(initialDir)
chooser.set_current_name("world")
chooser.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
response = chooser.run()
if response == gtk.RESPONSE_OK:
fls.append(chooser.get_filename()) # Returns the folder path if gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER is the action
else:
fls.append(None)
chooser.destroy()
gtk.main_quit()
gtk.idle_add(run_dlg)
gtk.main()
return fls[0]
else:
print "You currently need gtk to use an Open Folder Dialog!"
# End Open Folder Dialogs
# if sys.platform == "win32":
# try:
#
# (filename, customfilter, flags) = win32gui.GetSaveFileNameW(
# hwndOwner = display.get_wm_info()['window'],
# # InitialDir=minecraftSaveFileDir,
# Flags=win32con.OFN_EXPLORER | win32con.OFN_NOCHANGEDIR | win32con.OFN_OVERWRITEPROMPT,
# File=initialDir + os.sep + displayName,
# DefExt=fileFormat,
# Title=,
# Filter=,
# )
# except Exception, e:
# print "Error getting filename: {0!r}".format(e)
# return
#
# elif sys.platform == "darwin" and AppKit is not None:
# sp = AppKit.NSSavePanel.savePanel()
# sp.setDirectory_(initialDir)
# sp.setAllowedFileTypes_([fileFormat])
# # sp.setFilename_(self.editor.level.displayName)
#
# if sp.runModal() == 0:
# return; # pressed cancel
#
# filename = sp.filename()
# AppKit.NSApp.mainWindow().makeKeyWindow()
#
# else:
#
# filename = request_new_filename(prompt = "Save this schematic...",
# suffix = ".{0}".format(fileFormat),
# directory = initialDir,
# filename = displayName,
# pathname = None)
#
# return filename
win32_window_size = True
#=============================================================================
#=============================================================================
# DESKTOP ENVIRONMENTS AND OS SIDE WINDOW MANAGEMENT.
#
# The idea is to have a single object to deal with underling OS specific window management interface.
# This will help to save/restore MCEdit window sate, position and size.
#
# TODO:
# * Test on the actually unsupported Linux DEs.
# * Review WWindowHandler class for Windows.
# * Create a DWindowHandler class for Darwin (OSX).
# Window states
MINIMIZED = 0
NORMAL = 1
MAXIMIZED = 2
FULLSCREEN = 3
#=============================================================================
# Linux desktop environment detection.
#
# source: http://stackoverflow.com/questions/2035657/what-is-my-current-desktop-environment (http://stackoverflow.com/a/21213358)
#
# Tweaked, of course ;)
#
[docs]def get_desktop_environment():
# From http://stackoverflow.com/questions/2035657/what-is-my-current-desktop-environment
# and http://ubuntuforums.org/showthread.php?t=652320
# and http://ubuntuforums.org/showthread.php?t=652320
# and http://ubuntuforums.org/showthread.php?t=1139057
if sys.platform in ["win32", "cygwin"]:
return "windows"
elif sys.platform == "darwin":
return "mac"
else: # Most likely either a POSIX system or something not much common
ds = os.environ.get("DESKTOP_SESSION", None)
if ds in ('default', None):
ds = os.environ.get("XDG_CURRENT_DESKTOP", None)
if ds is not None: # easier to match if we doesn't have to deal with caracter cases
desktop_session = ds.lower()
found = re.findall(r"gnome|unity|cinnamon|mate|xfce4|lxde|fluxbox|blackbox|openbox|icewm|jwm|afterstep|trinity|kde", desktop_session, re.I)
if len(found) == 1:
return found[0]
elif len(found) > 1:
print "Houston? We have a problem...\n\nThe desktop environment can't be found: '%s' has been detected to be %s alltogeteher." % (ds, " and ".join((", ".join(found[:-1]), found[-1])))
return 'unknown'
# # Special cases ##
# Canonical sets $DESKTOP_SESSION to Lubuntu rather than LXDE if using LXDE.
# There is no guarantee that they will not do the same with the other desktop environments.
elif "xfce" in desktop_session or desktop_session.startswith("xubuntu"):
return "xfce4"
elif desktop_session.startswith("ubuntu"):
return "unity"
elif desktop_session.startswith("lubuntu"):
return "lxde"
elif desktop_session.startswith("kubuntu"):
return "kde"
elif desktop_session.startswith("razor"): # e.g. razorkwin
return "razor-qt"
elif desktop_session.startswith("wmaker"): # e.g. wmaker-common
return "windowmaker"
if os.environ.get('KDE_FULL_SESSION', None) == 'true':
return "kde"
elif os.environ.get('GNOME_DESKTOP_SESSION_ID', None):
if not "deprecated" in os.environ.get('GNOME_DESKTOP_SESSION_ID', None):
return "gnome2"
# From http://ubuntuforums.org/showthread.php?t=652320
elif is_running("xfce-mcs-manage"):
return "xfce4"
elif is_running("ksmserver"):
return "kde"
return "unknown"
[docs]def is_running(process):
# From http://www.bloggerpolis.com/2011/05/how-to-check-if-a-process-is-running-using-python/
# and http://richarddingwall.name/2009/06/18/windows-equivalents-of-ps-and-kill-commands/
try: # Linux/Unix
s = subprocess.Popen(["ps", "axw"], stdout=subprocess.PIPE)
except: # Windows
s = subprocess.Popen(["tasklist", "/v"], stdout=subprocess.PIPE)
for x in s.stdout:
if re.search(process, x):
return True
return False
#=============================================================================
# Window handling.
desktop_environment = get_desktop_environment()
DEBUG_WM = False
USE_WM = True
# Desktops settings
# Each entry in the platform sub-dictionaries represent which object is used to get/set the window metrics.
#
# For Linux:
# Valid entries are: position_gap, position_getter, position_setter, size_getter, size_setter and state.
# Entries can be omitted; default values will be used.
# For position_gap the default is (0, 0, False, False)
# For the other ones, the default is the Pygame window object.
#
# position_gap is used on some environment to restore the windows at the coords it was formerly.
# The two first values of the tuple are the amount of pixels to add to the window x and y coords.
# The two last ones tell whether these pixels shall be added only once (at program startup) or always.
#
desktops = {'linux2': {
'cinnamon': { # Actually, there's a bug when resizing on XCinnamon.
'position_setter': 'parent',
'position_getter': 'parent.parent',
'position_gap': (9, 8, True, True),
'state': 'parent'
},
'gnome': {
'position_setter': 'parent',
'position_getter': 'parent.parent',
'size_setter': 'parent',
'size_getter': 'parent',
'state': 'parent'
},
'kde': {
'position_setter': 'parent',
'position_getter': 'parent.parent.parent',
'state': 'parent'
},
'unity': {
'position_setter': 'parent',
'position_getter': 'parent.parent.parent',
'position_gap': (10, 10, False, False),
'size_setter': 'parent',
'state': 'parent'
}
},
# 'win32': {},
# 'darwin': {}
}
# The environments in the next definition need to be tested.
linux_unsuported = ('afterstep',
'blackbox',
'fluxbox',
'gnome2',
'icewm',
'jwm',
'lxde',
'mate',
'openbox',
'razor-qt',
'trinity',
'windowmaker',
'xfce4')
# Window handlers classes
[docs]class BaseWindowHandler:
"""Abstract class for the platform specific window handlers.
If initialized, this class casts a NotImplementedError."""
desk_env = desktop_environment
def __init__(self, *args, **kwargs):
"""..."""
if not len(kwargs):
raise NotImplementedError, "Abstract class."
self.mode = kwargs['mode']
[docs] def set_mode(self, size, mode):
"""Wrapper for pygame.display.set_mode()."""
display.set_mode(size, mode)
[docs] def get_root_rect(self):
"""..."""
raise NotImplementedError, "Abstract method."
[docs] def get_size(self):
"""..."""
raise NotImplementedError, "Abstract method."
[docs] def set_size(self, size, update=True):
"""..."""
raise NotImplementedError, "Abstract method."
[docs] def get_position(self):
"""..."""
raise NotImplementedError, "Abstract method."
[docs] def set_position(self, pos, update=True):
"""..."""
raise NotImplementedError, "Abstract method."
[docs] def get_state(self):
"""..."""
raise NotImplementedError, "Abstract method."
[docs] def set_state(self, state=NORMAL, size=(-1, -1), pos=(-1, -1), update=True):
"""..."""
raise NotImplementedError, "Abstract method."
[docs] def flush(self):
"""Just does nothing..."""
return
[docs] def sync(self):
"""Just does nothing..."""
return
[docs]class XWindowHandler(BaseWindowHandler):
"""Object to deal with XWindow managers (Linux)."""
desk_env = desktop_environment
def __init__(self, pos=(0, 0), size=(0, 0), mode=None):
"""Set up the internal handlers."""
BaseWindowHandler.__init__(self, pos=pos, size=size, mode=mode)
self.mode = mode
# setup the internal data, especially the Xlib object we need.
# Tests
if DEBUG_WM:
print "#" * 72
print "XWindowHandler.__init__"
print "Desktop environment:", desktop_environment
dis = self.display = Xlib.display.Display()
pygame_win = dis.create_resource_object('window', display.get_wm_info()['window'])
pygame_win_id = pygame_win.id
if DEBUG_WM:
root = dis.screen().root
active_wid_id = root.get_full_property(dis.intern_atom('_NET_ACTIVE_WINDOW'), Xlib.X.AnyPropertyType).value[0]
active_win = dis.create_resource_object('window', active_wid_id)
# Print pygame_win and active_win styff
for (win, name) in ((pygame_win, 'pygame_win'), (active_win, 'active_win')):
print "=" * 72
print "%s guts" % name, "(ID %s)" % win.id
print "-" * 72
print "* State"
prop = win.get_full_property(dis.intern_atom("_NET_WM_STATE"), 4)
print " ", prop
if prop:
print dir(prop)
print "* Geometry"
print " ", win.get_geometry()
parent = win.query_tree().parent
p = '%s.parent' % name
while parent.id != root.id:
print "-" * 72
print p, "ID", parent.id
print "* State"
prop = parent.get_full_property(dis.intern_atom("_NET_WM_STATE"), 4)
print " ", prop
if prop:
print dir(prop)
print "* Geometry"
print " ", parent.get_geometry()
parent = parent.query_tree().parent
p += ".parent"
# Size handlers
self.base_handler = pygame_win
self.base_handler_id = pygame_win.id
size = desktops['linux2'][self.desk_env].get('size_getter', None)
if size:
if DEBUG_WM:
print "size_getter.split('.')", size.split('.')
handler = pygame_win
for item in size.split('.'):
handler = getattr(handler.query_tree(), item)
self.sizeGetter = handler
else:
self.sizeGetter = pygame_win
size = desktops['linux2'][self.desk_env].get('size_setter', None)
if size:
if DEBUG_WM:
print "size_setter.split('.')", size.split('.')
handler = pygame_win
for item in size.split('.'):
handler = getattr(handler.query_tree(), item)
self.sizeSetter = handler
else:
self.sizeSetter = pygame_win
# Position handlers
pos = desktops['linux2'][self.desk_env].get('position_getter', None)
if pos:
if DEBUG_WM:
print "pos_getter.split('.')", pos.split('.')
handler = pygame_win
for item in pos.split('.'):
handler = getattr(handler.query_tree(), item)
self.positionGetter = handler
else:
self.positionGetter = pygame_win
pos = desktops['linux2'][self.desk_env].get('position_setter', None)
if pos:
if DEBUG_WM:
print "pos_setter.split('.')", pos.split('.')
handler = pygame_win
for item in pos.split('.'):
handler = getattr(handler.query_tree(), item)
self.positionSetter = handler
else:
self.positionSetter = pygame_win
# Position gap. Used to correct wrong positions on some environments.
self.position_gap = desktops['linux2'][self.desk_env].get('position_gap', (0, 0, False, False))
self.starting = True
self.gx, self.gy = 0, 0
# State handler
state = desktops['linux2'][self.desk_env].get('state', None)
if state:
if DEBUG_WM:
print "state.split('.')", state.split('.')
handler = pygame_win
for item in state.split('.'):
handler = getattr(handler.query_tree(), item)
self.stateHandler = handler
else:
self.stateHandler = pygame_win
if DEBUG_WM:
print "self.positionGetter:", self.positionGetter, 'ID:', self.positionGetter.id
print "self.positionSetter:", self.positionSetter, 'ID:', self.positionSetter.id
print "self.sizeGetter:", self.sizeGetter, 'ID:', self.sizeGetter.id
print "self.sizeSetter:", self.sizeSetter, 'ID:', self.sizeSetter.id
print "self.stateHandler:", self.stateHandler, 'ID:', self.stateHandler.id
print self.stateHandler.get_wm_state()
[docs] def get_root_rect(self):
"""Return a four values tuple containing the position and size of the very first OS window object."""
geom = self.display.screen().root.get_geometry()
return (geom.x, geom.y, geom.width, geom.height)
[docs] def get_size(self):
"""Return the window actual size as a tuple (width, height)."""
geom = self.sizeGetter.get_geometry()
if DEBUG_WM:
print "Actual size is", geom.width, geom.height
return (geom.width, geom.height)
[docs] def set_size(self, size, update=True):
"""Set the window size.
:size: list or tuple: the new size.
Raises a TypeError if something else than a list or a tuple is sent."""
if type(size) in (list, tuple):
# Call the Xlib object handling the size to update it.
if DEBUG_WM:
print "Setting size to", size
print "actual size", self.get_size()
self.sizeSetter.configure(width=size[0], height=size[1])
if update:
self.sync()
else:
# Raise a Type error.
raise TypeError, "%s is not a list or a tuple." % size
[docs] def get_position(self):
"""Return the window actual position as a tuple."""
geom = self.positionGetter.get_geometry()
x, y = geom.x, geom.y
# if DEBUG_WM:
# print "Actual position is", x, y
return (x, y)
[docs] def set_position(self, pos, update=True):
"""Set the window position.
:pos: list or tuple: the new position (x, y).
:update: bool: wheteher to call the internal sync method."""
if DEBUG_WM:
print "Setting position to", pos
if type(pos) in (list, tuple):
gx, gy = 0 or self.gx, 0 or self.gy
if self.starting:
gx, gy = self.position_gap[:2]
if self.position_gap[2]:
self.gx = gx
if self.position_gap[3]:
self.gy = gy
self.starting = False
# Call the Xlib object handling the position to update it.
self.positionSetter.configure(x=pos[0] + gx, y=pos[1] + gy)
if update:
self.sync()
else:
# Raise a Type error.
raise TypeError, "%s is not a list or a tuple." % pos
[docs] def get_state(self):
"""Return wheter the window is maximized or not, or minimized or full screen."""
state = self.stateHandler.get_full_property(self.display.intern_atom("_NET_WM_STATE"), 4)
# if DEBUG_WM:
# print "state_1.value", state.value
# print "max vert", self.display.intern_atom("_NET_WM_STATE_MAXIMIZED_VERT") ,self.display.intern_atom("_NET_WM_STATE_MAXIMIZED_VERT") in state.value
# print "max horz", self.display.intern_atom("_NET_WM_STATE_MAXIMIZED_HORZ"), self.display.intern_atom("_NET_WM_STATE_MAXIMIZED_HORZ") in state.value
if self.display.intern_atom("_NET_WM_STATE_MAXIMIZED_HORZ") in state.value and self.display.intern_atom("_NET_WM_STATE_MAXIMIZED_VERT") in state.value:
# if DEBUG_WM:
# print MAXIMIZED
return MAXIMIZED
elif self.display.intern_atom("_NET_WM_STATE_HIDEN") in state.value:
# if DEBUG_WM:
# print MINIMIZED
return MINIMIZED
elif self.display.intern_atom("_NET_WM_STATE_FULLSCREEN") in state.value:
# if DEBUG_WM:
# print FULLSCREEN
return FULLSCREEN
# if DEBUG_WM:
# print NORMAL
return NORMAL
[docs] def set_state(self, state=NORMAL, size=(-1, -1), pos=(-1, -1), update=True):
"""Set wheter the window is maximized or not, or minimized or full screen.
If no argument is given, assume the state will be windowed and not maximized.
If arguments are given, only the first is relevant. The other ones are ignored.
** Only maximized and normal states are implemented for now. **
:state: valid arguments:
'minimized', MINIMIZED, 0.
'normal', NORMAL, 1: windowed, not maximized.
'maximized', MAXIMIZED, 2.
'fullscreen, FULLSCREEN, 3.
:size: list, tuple: the new size; if (-1, -1) self.get_size() is used.
If one element is -1 it is replaced by the corresponding valur from self.get_size().
:pos: list, tuple: the new position; if (-1, -1), self.get_position is used.
If one element is -1 it is replaced by the corresponding valur from self.get_position().
:update: bool: whether to call the internal flush method."""
if state not in (0, MINIMIZED, 'minimized', 1, NORMAL, 'normal', 2, MAXIMIZED, 'maximized', 3, FULLSCREEN, 'fullscreen'):
# Raise a value error.
raise ValueError, "Invalid state argument: %s is not a correct value" % state
if type(size) not in (list, tuple):
raise TypeError, "Invalid size argument: %s is not a list or a tuple."
if type(pos) not in (list, tuple):
raise TypeError, "Invalid pos argument: %s is not a list or a tuple."
if state in (1, NORMAL, 'normal'):
size = list(size)
sz = self.get_size()
if size[0] == -1:
size[0] = sz[0]
if size[1] == -1:
size[1] = sz[1]
pos = list(pos)
ps = self.get_position()
if pos[0] == -1:
pos[0] = ps[0]
if pos[1] == -1:
pos[1] = ps[1]
self.set_mode(size, self.mode)
self.set_position(pos)
elif state in (0, MINIMIZED, 'minimized'):
pass
elif state in (2, MAXIMIZED, 'maximized'):
data = [1, self.display.intern_atom("_NET_WM_STATE_MAXIMIZED_VERT", False), self.display.intern_atom("_NET_WM_STATE_MAXIMIZED_HORZ", False)]
data = (data + ([0] * (5 - len(data))))[:5]
if DEBUG_WM:
print self.stateHandler.get_wm_state()
print "creating event", Xlib.protocol.event.ClientMessage
print dir(self.stateHandler)
x_event = Xlib.protocol.event.ClientMessage(window=self.stateHandler, client_type=self.display.intern_atom("_NET_WM_STATE", False), data=(32, (data)))
if DEBUG_WM:
print "sending event"
self.display.screen().root.send_event(x_event, event_mask=Xlib.X.SubstructureRedirectMask)
if DEBUG_WM:
print self.stateHandler.get_wm_state()
elif state in (3, FULLSCREEN, 'fullscreen'):
pass
if update:
self.flush()
[docs] def flush(self):
"""Wrapper around Xlib.Display.flush()"""
if DEBUG_WM:
print "* flushing display"
self.display.flush()
[docs] def sync(self):
"""Wrapper around Xlib.Display.sync()"""
if DEBUG_WM:
print "* syncing display"
self.display.sync()
#=======================================================================
# WARNING: This class has been built on Linux using wine.
# Please review this code and change it consequently before using it without '--debug-wm' switch!
[docs]class WWindowHandler(BaseWindowHandler):
"""Object to deal with Microsoft Window managers."""
desk_env = desktop_environment
def __init__(self, pos=(0, 0), size=(0, 0), mode=None):
"""Set up the internal handlers."""
BaseWindowHandler.__init__(self, pos=pos, size=size, mode=mode)
# Tests
if DEBUG_WM:
print "#" * 72
print "WWindowHandler.__init__"
print "Desktop environment:", desktop_environment
for item in dir(win32con):
if 'maxim' in item.lower() or 'minim' in item.lower() or 'full' in item.lower():
print item, getattr(win32con, item)
self.base_handler = display
self.base_handler_id = display.get_wm_info()['window']
if platform.dist() == ('', '', ''):
# We're running on a native Windows.
def set_mode(self, size, mode):
"""Wrapper for pygame.display.set_mode()."""
# Windows pygame implementation seem to work on the display mode and size on it's own...
return
else:
# We're running on wine.
[docs] def set_mode(self, size, mode):
"""Wrapper for pygame.display.set_mode()."""
if getattr(self, 'wine_state_fix', False):
self.set_size(size)
self.wine_state_fix = True
else:
self.wine_state_fix = False
[docs] def get_root_rect(self):
"""Return a four values tuple containing the position and size of the very first OS window object."""
flags, showCmd, ptMin, ptMax, rect = win32gui.GetWindowPlacement(win32gui.GetDesktopWindow())
return rect
[docs] def get_size(self):
"""Return the window actual size as a tuple (width, height)."""
flags, showCmd, ptMin, ptMax, rect = win32gui.GetWindowPlacement(self.base_handler_id)
w = rect[2] - rect[0]
h = rect[3] - rect[1]
return (w, h)
[docs] def set_size(self, size, update=True):
"""Set the window size.
:size: list or tuple: the new size.
:mode: bool: (re)set the pygame.display mode; self.mode must be a pygame display mode object.
Raises a TypeError if something else than a list or a tuple is sent."""
if type(size) in (list, tuple):
w, h = size
cx, cy = win32gui.GetCursorPos()
if DEBUG_WM:
print "Settin size to", size
print "actual size", self.get_size()
print "actual position", self.get_position()
print 'cursor pos', cx, cy
flags, showCmd, ptMin, ptMax, rect = win32gui.GetWindowPlacement(self.base_handler_id)
if DEBUG_WM:
print "set_size rect", rect, "ptMin", ptMin, "ptMax", ptMax, "flags", flags
x = rect[0]
y = rect[1]
rect = (x, y, x + w, y + h)
win32gui.SetWindowPlacement(self.base_handler_id, (0, showCmd, ptMin, ptMax, rect))
else:
# Raise a Type error.
raise TypeError, "%s is not a list or a tuple." % repr(size)
[docs] def get_position(self):
"""Return the window actual position as a tuple."""
(flags, showCmd, ptMin, ptMax, rect) = win32gui.GetWindowPlacement(self.base_handler_id)
x, y, r, b = rect
return (x, y)
[docs] def set_position(self, pos, update=True):
"""Set the window position.
:pos: list or tuple: the new position (x, y)."""
if DEBUG_WM:
print "Setting position to", pos
if type(pos) in (list, tuple):
self.first_pos = False
x, y = pos
if update:
flags, showCmd, ptMin, ptMax, rect = win32gui.GetWindowPlacement(self.base_handler_id)
if DEBUG_WM:
print "set_position rect", rect, "ptMin", ptMin, "ptMax", ptMax
realW = rect[2] - rect[0]
realH = rect[3] - rect[1]
if DEBUG_WM:
print 'rect[0]', rect[0], 'rect[1]', rect[1]
print 'realW', realW, 'realH', realH
print 'cursor pos', win32gui.GetCursorPos()
rect = (x, y, x + realW, y + realH)
win32gui.SetWindowPlacement(self.base_handler_id, (0, showCmd, ptMin, ptMax, rect))
else:
# Raise a Type error.
raise TypeError, "%s is not a list or a tuple." % repr(pos)
[docs] def get_state(self):
"""Return wheter the window is maximized or not, or minimized or full screen."""
flags, state, ptMin, ptMax, rect = win32gui.GetWindowPlacement(self.base_handler_id)
if DEBUG_WM:
print "state", state
if state == win32con.SW_MAXIMIZE:
return MAXIMIZED
elif state == win32con.SW_MINIMIZE:
return MINIMIZED
return NORMAL
[docs] def set_state(self, state=NORMAL, size=(-1, -1), pos=(-1, -1), update=True):
"""Set wheter the window is maximized or not, or minimized or full screen.
If no argument is given, assume the state will be windowed and not maximized.
If arguments are given, only the first is relevant. The other ones are ignored.
** Only maximized and normal states are implemented for now. **
:state: valid arguments:
'minimized', MINIMIZED, 0.
'normal', NORMAL, 1: windowed, not maximized.
'maximized', MAXIMIZED, 2.
'fullscreen, FULLSCREEN, 3.
:size: list, tuple: the new size; if (-1, -1) self.get_size() is used.
If one element is -1 it is replaced by the corresponding valur from self.get_size().
:pos: list, tuple: the new position; if (-1, -1), self.get_position is used.
If one element is -1 it is replaced by the corresponding valur from self.get_position().
:update: bool: whether to call the internal flush method."""
if state not in (0, MINIMIZED, 'minimized', 1, NORMAL, 'normal', 2, MAXIMIZED, 'maximized', 3, FULLSCREEN, 'fullscreen'):
# Raise a value error.
raise ValueError, "Invalid state argument: %s is not a correct value" % state
if type(size) not in (list, tuple):
raise TypeError, "Invalid size argument: %s is not a list or a tuple."
if type(pos) not in (list, tuple):
raise TypeError, "Invalid pos argument: %s is not a list or a tuple."
if state in (1, NORMAL, 'normal'):
size = list(size)
sz = self.get_size()
if size[0] == -1:
size[0] = sz[0]
if size[1] == -1:
size[1] = sz[1]
pos = list(pos)
ps = self.get_position()
if pos[0] == -1:
pos[0] = ps[0]
if pos[1] == -1:
pos[1] = ps[1]
self.set_mode(size, self.mode)
self.set_position(pos)
elif state in (0, MINIMIZED, 'minimized'):
pass
elif state in (2, MAXIMIZED, 'maximized'):
win32gui.ShowWindow(self.base_handler_id, win32con.SW_MAXIMIZE)
elif state in (3, FULLSCREEN, 'fullscreen'):
pass
WindowHandler = None
[docs]def setupWindowHandler():
"""'Link' the corresponding window handler class to WindowHandler."""
# Don't initialize the window handler here.
# We need MCEdit display objects to get the right object.
global WindowHandler
if USE_WM:
log.warn("Initializing window management...")
if sys.platform == 'linux2':
if XWindowHandler.desk_env == 'unknown':
log.warning("Your desktop environment could not be determined. The support for window sizing/moving is not availble.")
elif XWindowHandler.desk_env in linux_unsuported:
log.warning("Your desktop environment is not yet supported for window sizing/moving.")
else:
WindowHandler = XWindowHandler
log.info("XWindowHandler initialized.")
elif sys.platform == 'win32':
WindowHandler = WWindowHandler
log.info("WWindowHandler initialized.")
return WindowHandler
# setupWindowHandler()