Skip to content

Commit

Permalink
#545: honour double-click time (X11 and win32) and distance (X11 only):
Browse files Browse the repository at this point in the history
* add "double_click.time" and "double_click.distance" caps
* add ability to run gui class to directly see the values we get from the OS
* add them to xpra info
* add this info to bug reports
* add code to patch "xsettings-blob" on the fly to add those values
* trigger fake "xsettings" update to ensure values get applied
* add test app to view current settings and test them

git-svn-id: https://xpra.org/svn/Xpra/trunk@7613 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Sep 15, 2014
1 parent 6a00785 commit 6e55a2a
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 8 deletions.
69 changes: 69 additions & 0 deletions src/tests/xpra/test_apps/test_double_click.py
@@ -0,0 +1,69 @@
#!/usr/bin/env python
# This file is part of Xpra.
# Copyright (C) 2013 Antoine Martin <antoine@devloop.org.uk>

import sys
import pygtk
pygtk.require('2.0')
import gtk
import gobject


class TestForm(object):

def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("destroy", gtk.main_quit)
self.window.set_default_size(320, 200)
self.window.set_border_width(20)

vbox = gtk.VBox()
self.info = gtk.Label("")
self.show_click_settings()
gobject.timeout_add(1000, self.show_click_settings)
vbox.pack_start(self.info, False, False, 0)
self.label = gtk.Label("Ready")
vbox.pack_start(self.label, False, False, 0)

self.eventbox = gtk.EventBox()
self.eventbox.connect('button-press-event', self.button_press_event)
self.eventbox.add_events(gtk.gdk.BUTTON_PRESS_MASK)
self.eventbox.add_events(gtk.gdk.BUTTON_RELEASE_MASK)
vbox.pack_start(self.eventbox, True, True, 0)

self.window.add(vbox)
self.window.show_all()

def show_click_settings(self):
root = gtk.gdk.get_default_root_window()
screen = root.get_screen()
#use undocumented constants found in source:
try:
t = screen.get_setting("gtk-double-click-time")
except:
t = ""
try:
d = screen.get_setting("gtk-double-click-distance")
except:
d = ""
self.info.set_text("Time (ms): %s, Distance: %s" % (t, d))
return True

def button_press_event(self, obj, event):
if event.type == gtk.gdk._3BUTTON_PRESS:
self.label.set_text("Triple Click!")
elif event.type == gtk.gdk._2BUTTON_PRESS:
self.label.set_text("Double Click!")
elif event.type == gtk.gdk.BUTTON_PRESS:
self.label.set_text("Click")
else:
self.label.set_text("Unexpected event: %s" % event)

def main():
TestForm()
gtk.main()


if __name__ == "__main__":
main()
sys.exit(0)
2 changes: 2 additions & 0 deletions src/xpra/client/gtk_base/bug_report.py
Expand Up @@ -110,6 +110,7 @@ def get_gl_info():
get_gl_info = None
from xpra.net.net_util import get_info as get_net_info
from xpra.platform.paths import get_info as get_path_info
from xpra.platform.gui import get_info as get_gui_info
from xpra.version_util import get_version_info, get_platform_info, get_host_info
def get_sys_info():
d = {
Expand All @@ -124,6 +125,7 @@ def get_sys_info():
"host" : get_host_info(),
"paths" : get_path_info(),
"gtk" : get_gtk_version_info(),
"gui" : get_gui_info(),
"user" : get_user_info(),
"env" : os.environ,
"config" : read_xpra_defaults(),
Expand Down
4 changes: 3 additions & 1 deletion src/xpra/client/ui_client_base.py
Expand Up @@ -30,7 +30,7 @@
from xpra.client.keyboard_helper import KeyboardHelper
from xpra.platform import set_application_name
from xpra.platform.features import MMAP_SUPPORTED, SYSTEM_TRAY_SUPPORTED, CLIPBOARD_WANT_TARGETS, CLIPBOARD_GREEDY, CLIPBOARDS
from xpra.platform.gui import init as gui_init, ready as gui_ready, get_native_notifier_classes, get_native_tray_classes, get_native_system_tray_classes, get_native_tray_menu_helper_classes, ClientExtras
from xpra.platform.gui import init as gui_init, ready as gui_ready, get_double_click_time, get_double_click_distance, get_native_notifier_classes, get_native_tray_classes, get_native_system_tray_classes, get_native_tray_menu_helper_classes, ClientExtras
from xpra.codecs.codec_constants import get_PIL_decodings
from xpra.codecs.loader import codec_versions, has_codec, get_codec, PREFERED_ENCODING_ORDER, ALL_NEW_ENCODING_NAMES_TO_OLD, OLD_ENCODING_NAMES_TO_NEW, PROBLEMATIC_ENCODINGS
from xpra.codecs.video_helper import getVideoHelper, NO_GFX_CSC_OPTIONS
Expand Down Expand Up @@ -910,6 +910,8 @@ def make_hello(self):
"notifications" : self.client_supports_notifications,
"cursors" : self.client_supports_cursors,
"bell" : self.client_supports_bell,
"double_click.time" : get_double_click_time(),
"double_click.distance" : get_double_click_distance(),
"sound.server_driven" : True,
"encoding.client_options" : True,
"encoding_client_options" : True,
Expand Down
65 changes: 64 additions & 1 deletion src/xpra/platform/gui.py
Expand Up @@ -4,6 +4,7 @@
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import sys

_init_done = False
def init():
Expand Down Expand Up @@ -45,12 +46,39 @@ def system_bell(*args):
def get_native_notifier_classes():
return []

def get_double_click_time():
return -1

def get_double_click_distance():
return -1

def gl_check():
return None #no problem

take_screenshot = None
ClientExtras = None


def get_info_base():
def fname(v):
try:
return v.__name__
except:
return str(v)
def fnames(l):
return [fname(x) for x in l]
return {
"native_tray_menu_helpers" : fnames(get_native_tray_menu_helper_classes()),
"native_trays" : fnames(get_native_tray_classes()),
"native_system_trays" : fnames(get_native_system_tray_classes()),
"system_bell" : fname(system_bell),
"native_notifiers" : fnames(get_native_notifier_classes()),
"double_click.time" : get_double_click_time(),
"double_click.distance" : get_double_click_distance(),
}
get_info = get_info_base


from xpra.platform import platform_import
platform_import(globals(), "gui", False,
"do_ready",
Expand All @@ -62,4 +90,39 @@ def gl_check():
"get_native_tray_classes",
"get_native_system_tray_classes",
"get_native_notifier_classes",
"system_bell")
"get_double_click_time", "get_double_click_distance",
"system_bell",
"get_info")


def main():
from xpra.platform import init as platform_init,clean
from xpra.util import nonl, pver
try:
platform_init("GUI-Properties")
verbose = "-v" in sys.argv or "--verbose" in sys.argv
if verbose:
from xpra.log import get_all_loggers
for x in get_all_loggers():
x.enable_debug()

#naughty, but how else can I hook this up?
import os
if os.name=="posix":
try:
from xpra.x11.gtk_x11 import gdk_display_source
assert gdk_display_source
except:
from xpra.x11.gtk3_x11 import gdk_display_source #@Reimport
assert gdk_display_source

i = get_info()
for k in sorted(i.keys()):
v = i[k]
print("* %s : %s" % (k.ljust(32), nonl(pver(v))))
finally:
clean()


if __name__ == "__main__":
sys.exit(main())
7 changes: 7 additions & 0 deletions src/xpra/platform/win32/gui.py
Expand Up @@ -59,6 +59,13 @@ def gl_check():
return "disabled when running under wine"
return None

def get_double_click_time():
try:
import win32gui #@UnresolvedImport
return win32gui.GetDoubleClickTime()
except:
return 0


class ClientExtras(object):
def __init__(self, client, opts):
Expand Down
39 changes: 39 additions & 0 deletions src/xpra/platform/xposix/gui.py
Expand Up @@ -41,6 +41,33 @@ def get_native_system_tray_classes():
return get_native_tray_classes()


def _get_xsettings():
try:
from xpra.x11.xsettings import XSettingsHelper
xsh = XSettingsHelper()
return xsh.get_settings()
except Exception as e:
log("_get_xsettings error: %s", e)
return None

def _get_xsettings_int(name, default_value):
s = _get_xsettings()
if not s:
return default_value
from xpra.x11.xsettings_prop import XSettingsTypeInteger
_, values = s
for setting_type, prop_name, value, _ in values:
if setting_type==XSettingsTypeInteger and prop_name==name:
return value
return default_value

def get_double_click_time():
return _get_xsettings_int("Net/DoubleClickTime", -1)

def get_double_click_distance():
return _get_xsettings_int("Net/DoubleClickDistance", -1)


def system_bell(window, device, percent, pitch, duration, bell_class, bell_id, bell_name):
global device_bell
if device_bell is False:
Expand All @@ -62,6 +89,18 @@ def x11_bell():
return False


def get_info():
from xpra.platform.gui import get_info_base
i = get_info_base()
s = _get_xsettings()
if s:
serial, values = s
i["xsettings.serial"] = serial
for _,name,value,_ in values:
i["xsettings.%s" % name] = value
return i


class ClientExtras(object):
def __init__(self, client, opts):
self.client = client
Expand Down
26 changes: 21 additions & 5 deletions src/xpra/server/server_base.py
Expand Up @@ -69,6 +69,9 @@ def __init__(self):
self.cursors = False
self.default_dpi = 96
self.dpi = 96
#duplicated from Server Source...
self.double_click_time = -1
self.double_click_distance = -1
self.supports_clipboard = False
self.supports_dbus_proxy = False
self.dbus_helper = None
Expand Down Expand Up @@ -520,7 +523,8 @@ def hello_oked(self, proto, packet, c, auth_caps):
elif not ss.share:
self.disconnect_client(p, NEW_CLIENT, "this client had not enabled sharing")
disconnected += 1
share_count += 1
else:
share_count += 1

if detach_request:
self.disconnect_client(proto, DONE, "%i other clients have been disconnected" % disconnected)
Expand All @@ -529,11 +533,23 @@ def hello_oked(self, proto, packet, c, auth_caps):
if not is_request and ui_client:
if share_count>0:
log.info("sharing with %s other client(s)", share_count)
self.dpi = c.intget("dpi", self.default_dpi)
self.dpi = self.default_dpi
self.double_click_time = -1
self.double_click_distance = -1
else:
self.dpi = c.intget("dpi", self.default_dpi)
self.double_click_time = c.intget("double_click.time", -1)
self.double_click_distance = c.intget("double_click.distance", -1)
#if we're not sharing, reset all the settings:
reset = share_count==0
#some non-posix clients never send us 'resource-manager' settings
#so just use a fake one to ensure the dpi gets applied:
if self.dpi>0:
#some non-posix clients never send us 'resource-manager' settings
#so just use a fake one to ensure the dpi gets applied:
self.update_server_settings({'resource-manager' : ""}, reset=(share_count==0))
self.update_server_settings({'resource-manager' : ""}, reset=reset)
#same for xsettings and double click settings:
#fake an empty xsettings update
if self.double_click_time>0 or self.double_click_distance>0:
self.update_server_settings({"xsettings-blob" : (0, [])}, reset=reset)
#max packet size from client (the biggest we can get are clipboard packets)
proto.max_packet_size = 1024*1024 #1MB
proto.send_aliases = c.dictget("aliases")
Expand Down
6 changes: 6 additions & 0 deletions src/xpra/server/source.py
Expand Up @@ -323,6 +323,8 @@ def init_vars(self):
self.notify_startup_complete = False
self.control_commands = []
self.supports_transparency = False
self.double_click_time = -1
self.double_click_distance = -1
#what we send back in hello packet:
self.ui_client = True
self.wants_aliases = True
Expand Down Expand Up @@ -539,6 +541,8 @@ def parse_batch_int(value, varname):
self.notify_startup_complete = c.boolget("notify-startup-complete")
self.control_commands = c.strlistget("control_commands")
self.supports_transparency = HAS_ALPHA and c.boolget("encoding.transparency")
self.double_click_time = c.intget("double_click.time")
self.double_click_distance = c.intget("double_click.distance")

self.desktop_size = c.intpair("desktop_size")
if self.desktop_size is not None:
Expand Down Expand Up @@ -1099,6 +1103,8 @@ def battr(k, name):
"lz4", "lzo"):
battr(prop, prop)
for prop, name in {"clipboard_enabled" : "clipboard",
"double_click_time" : "double_click.time",
"double_click_distance" : "double_click.distance",
"send_windows" : "windows",
"send_cursors" : "cursors",
"send_notifications" : "notifications",
Expand Down
19 changes: 18 additions & 1 deletion src/xpra/x11/server.py
Expand Up @@ -814,15 +814,17 @@ def update_server_settings(self, settings, reset=False):
settingslog("ignoring xsettings update: %s", settings)
return
if reset:
#FIXME: preserve serial? (what happens when we change values which had the same serial?)
self.reset_settings()
self._settings = self.default_xsettings or {}
old_settings = dict(self._settings)
settingslog("server_settings: old=%s, updating with=%s", nonl(old_settings), nonl(settings))
settingslog("overrides: dpi=%s, double click time=%s, double click distance=%s", self.dpi, self.double_click_time, self.double_click_distance)
self._settings.update(settings)
root = gtk.gdk.get_default_root_window()
for k, v in settings.items():
#cook the "resource-manager" value to add the DPI:
if k == "resource-manager" and self.dpi>0:
if k=="resource-manager" and self.dpi>0:
value = v.decode("utf-8")
#parse the resources into a dict:
values={}
Expand All @@ -845,6 +847,21 @@ def update_server_settings(self, settings, reset=False):
self._settings["resource-manager"] = value
v = value.encode("utf-8")

#cook xsettings to add double-click settings:
#(as those may not be present in xsettings on some platforms.. like win32 and osx)
if k=="xsettings-blob" and (self.double_click_time>0 or self.double_click_distance>0):
from xpra.x11.xsettings_prop import XSettingsTypeInteger
def set_xsettings_int(name, value):
#remove existing one, if any:
serial, values = v
new_values = [(_t,_n,_v,_s) for (_t,_n,_v,_s) in values if _n!=name]
new_values.append((XSettingsTypeInteger, name, value, 0))
return serial, new_values
if self.double_click_time>0:
v = set_xsettings_int("Net/DoubleClickTime", self.double_click_time)
if self.double_click_distance>0:
v = set_xsettings_int("Net/DoubleClickDistance", self.double_click_distance)

if k not in old_settings or v != old_settings[k]:
def root_set(p):
settingslog("server_settings: setting %s to %s", nonl(p), nonl(v))
Expand Down
1 change: 1 addition & 0 deletions src/xpra/x11/x11_server_base.py
Expand Up @@ -427,6 +427,7 @@ def delfile(msg):

def _process_server_settings(self, proto, packet):
settings = packet[1]
log("process_server_settings: %s", settings)
self.update_server_settings(settings)

def update_server_settings(self, settings, reset=False):
Expand Down

0 comments on commit 6e55a2a

Please sign in to comment.