xpra icon
Bug tracker and wiki

Ticket #772: emulate-moveresize-v2.patch

File emulate-moveresize-v2.patch, 14.2 KB (added by Antoine Martin, 5 years ago)

updated patch for trunk

  • xpra/client/client_window_base.py

     
    476476        return self._id
    477477
    478478    def do_motion_notify_event(self, event):
    479         if self._client.readonly:
     479        event = self.preprocess_motion_event(event)
     480        if not event or self._client.readonly:
    480481            return
    481482        pointer, modifiers, buttons = self._pointer_modifiers(event)
    482483        wid = self.get_mouse_event_wid()
     
    484485        self._client.send_mouse_position(["pointer-position", wid,
    485486                                          pointer, modifiers, buttons])
    486487
     488    def preprocess_motion_event(self, event):
     489        #allows subclasses to mess with events
     490        return event
     491
     492    def preprocess_button_action(self, button, event, depressed):
     493        return button, event, depressed
     494
    487495    def _button_action(self, button, event, depressed):
    488         if self._client.readonly:
     496        r = self.preprocess_button_action(button, event, depressed)
     497        if not r or self._client.readonly:
    489498            return
     499        button, event, depressed = r
    490500        pointer, modifiers, buttons = self._pointer_modifiers(event)
    491501        wid = self.get_mouse_event_wid()
    492502        mouselog("_button_action(%s, %s, %s) wid=%s / focus=%s, pointer=%s, modifiers=%s, buttons=%s", button, event, depressed, self._id, self._client._focused, pointer, modifiers, buttons)
  • xpra/client/gtk_base/gtk_client_base.py

     
    251251            ms += ["shaded", "bypass-compositor", "strut", "fullscreen-monitors"]
    252252        log("metadata.supported: %s", ms)
    253253        capabilities["metadata.supported"] = ms
    254         #we need the bindings to support initiate-moveresize (posix only for now):
    255         from xpra.client.gtk_base.gtk_client_window_base import HAS_X11_BINDINGS
     254        from xpra.client.gtk_base.gtk_client_window_base import HAS_X11_BINDINGS, EMULATE_MOVERESIZE
    256255        from xpra.client.window_backing_base import DELTA_BUCKETS
    257         capabilities["window.initiate-moveresize"] = HAS_X11_BINDINGS
     256        capabilities["window.initiate-moveresize"] = HAS_X11_BINDINGS or EMULATE_MOVERESIZE
    258257        updict(capabilities, "encoding", {
    259258                    "icons.greedy"      : True,         #we don't set a default window icon any more
    260259                    "icons.size"        : (64, 64),     #size we want
  • xpra/client/gtk_base/gtk_client_window_base.py

     
    2525from xpra.gtk_common.gtk_util import get_pixbuf_from_data
    2626from xpra.gtk_common.keymap import KEY_TRANSLATIONS
    2727from xpra.client.client_window_base import ClientWindowBase
    28 from xpra.platform.gui import get_window_frame_sizes, set_fullscreen_monitors, set_shaded
     28from xpra.platform.gui import add_window_hooks, remove_window_hooks
     29from xpra.platform.gui import get_window_frame_sizes, set_fullscreen_monitors, set_shaded, set_capture, release_capture
    2930from xpra.codecs.argb.argb import unpremultiply_argb, bgra_to_rgba    #@UnresolvedImport
    30 from xpra.platform.gui import add_window_hooks, remove_window_hooks
    3131gtk     = import_gtk()
    3232gdk     = import_gdk()
    3333cairo   = import_cairo()
     
    5959            log.info("failed to setup workspace hooks: %s", e)
    6060    except ImportError:
    6161        pass
     62EMULATE_MOVERESIZE = os.environ.get("XPRA_EMULATE_MOVERESIZE", str(int(not HAS_X11_BINDINGS)))=="1"
    6263
     64MOVERESIZE_SIZE_TOPLEFT      = 0
     65MOVERESIZE_SIZE_TOP          = 1
     66MOVERESIZE_SIZE_TOPRIGHT     = 2
     67MOVERESIZE_SIZE_RIGHT        = 3
     68MOVERESIZE_SIZE_BOTTOMRIGHT  = 4
     69MOVERESIZE_SIZE_BOTTOM       = 5
     70MOVERESIZE_SIZE_BOTTOMLEFT   = 6
     71MOVERESIZE_SIZE_LEFT         = 7
     72MOVERESIZE_MOVE              = 8
     73MOVERESIZE_SIZE_KEYBOARD     = 9
     74MOVERESIZE_MOVE_KEYBOARD     = 10
     75MOVERESIZE_CANCEL            = 11
     76MOVERESIZE_DIRECTION_STRING = {
     77                    MOVERESIZE_SIZE_TOPLEFT     : "SIZE_TOPLEFT",
     78                    MOVERESIZE_SIZE_TOP         : "SIZE_TOP",
     79                    MOVERESIZE_SIZE_TOPRIGHT    : "SIZE_TOPRIGHT",
     80                    MOVERESIZE_SIZE_RIGHT       : "SIZE_RIGHT",
     81                    MOVERESIZE_SIZE_BOTTOMRIGHT : "SIZE_BOTTOMRIGHT",
     82                    MOVERESIZE_SIZE_BOTTOM      : "SIZE_BOTTOM",
     83                    MOVERESIZE_SIZE_BOTTOMLEFT  : "SIZE_BOTTOMLEFT",
     84                    MOVERESIZE_SIZE_LEFT        : "SIZE_LEFT",
     85                    MOVERESIZE_MOVE             : "MOVE",
     86                    MOVERESIZE_SIZE_KEYBOARD    : "SIZE_KEYBOARD",
     87                    MOVERESIZE_MOVE_KEYBOARD    : "MOVE_KEYBOARD",
     88                    MOVERESIZE_CANCEL           : "CANCEL",
     89                    }
    6390
     91
    6492#window types we map to POPUP rather than TOPLEVEL
    6593POPUP_TYPE_HINTS = set((
    6694                    #"DIALOG",
     
    108136        self._window_state = {}
    109137        self._resize_counter = 0
    110138        self._can_set_workspace = HAS_X11_BINDINGS and CAN_SET_WORKSPACE
     139        self._moveresize = None
    111140        ClientWindowBase.init_window(self, metadata)
    112141
    113142    def _is_popup(self, metadata):
     
    169198
    170199    def setup_window(self):
    171200        self.set_app_paintable(True)
    172         self.add_events(self.WINDOW_EVENT_MASK)
     201        self.add_events(self.WINDOW_EVENT_MASK)        #add platform hooks
    173202        #add platform hooks
    174203        self.connect("realize", self.on_realize)
    175204        self.connect('unrealize', self.on_unrealize)
     
    215244                        x = max(0, x-dx)
    216245                    if dy:
    217246                        y = max(0, y-dy)
     247                #self.realize()
     248                #win = self.get_window()
     249                #rox, roy = win.get_root_origin()
     250                #ox, oy = win.get_origin()
     251                #dx = ox-rox
     252                #dy = oy-roy
     253                #log.info("position=%s, root-origin=%s, origin=%s, frame_extents=%s", (x, y), (rox, roy), (ox, oy), win.get_frame_extents())
     254                #log.info("frame=%s", [getattr(self, a) for a in ("frame", "frame_top", "frame_right", "frame_bottom", "frame_left")])
     255                #log.info("initial position offsets: %s, %s", dx, dy)
    218256            self.move(x, y)
    219257        self.set_default_size(*self._size)
    220258
     
    568606        return self._window_workspace
    569607
    570608
    571     def initiate_moveresize(self, x_root, y_root, direction, button, source_indication):
    572         statelog("initiate_moveresize%s", (x_root, y_root, direction, button, source_indication))
    573         assert HAS_X11_BINDINGS, "cannot handle initiate-moveresize without X11 bindings"
    574         event_mask = SubstructureNotifyMask | SubstructureRedirectMask
    575         with xsync:
    576             from xpra.gtk_common.gobject_compat import get_xid
     609    def preprocess_motion_event(self, event):
     610        #we override here so we can emulate moveresize:
     611        log.info("preprocess_motion_event(%s) we have moveresize: %s", event, self._moveresize)
     612        if self._moveresize:
     613            px, py = int(event.x), int(event.y)
     614            opx, opy = self._moveresize[1]                  #original pointer position
     615            dx = px-opx
     616            dy = py-opy
     617            owx, owy = self._moveresize[2]                  #original window position
     618            cwx, cwy = self.get_position()                  #current position
    577619            root = self.get_window().get_screen().get_root_window()
    578             root_xid = get_xid(root)
    579             xwin = get_xid(self.get_window())
    580             X11Core.UngrabPointer()
    581             X11Window.sendClientMessage(root_xid, xwin, False, event_mask, "_NET_WM_MOVERESIZE",
    582                   x_root, y_root, direction, button, source_indication)
     620            maxx, maxy = root.get_size()
     621            direction = self._moveresize[0]
     622            if direction==MOVERESIZE_MOVE:
     623                #FIXME: moving the window triggers new motion events..
     624                wx = max(0, owx + dx)
     625                wx = min(maxx, wx)
     626                wy = max(0, owy + dy)
     627                wy = min(maxy, wy)
     628                log.info("%s(%s with %s)=%s", MOVERESIZE_DIRECTION_STRING.get(direction), (owx, owy), (dx, dy), (wx, wy))
     629                if wx!=cwx or wy!=cwy:
     630                    self.move(wx, wy)
     631            else:
     632                #must be a move:
     633                oww, owh = self._moveresize[3]                  #original window size
     634                cww, cwh = self.get_size()                      #current size
     635                #log.info("delta=%s, original=%s, current=%s", (dx, dy), owx, owy, )
     636                wx, wy, ww, wh = owx, owy, oww, owh             #default to current
     637                if direction==MOVERESIZE_SIZE_BOTTOMRIGHT:
     638                    ww = oww + dx
     639                    wh = owh + dy
     640                elif direction==MOVERESIZE_SIZE_RIGHT:
     641                    ww = oww + dx
     642                elif direction==MOVERESIZE_SIZE_BOTTOM:
     643                    wh = owh + dy
     644                elif direction==MOVERESIZE_SIZE_TOP:
     645                    wy = max(0, owy + dy)
     646                    wh = (owy + owh) - wy
     647                elif direction==MOVERESIZE_SIZE_TOPRIGHT:
     648                    wy = max(0, owy + dy)
     649                    wh = (owy + owh) - wy
     650                    ww = oww + dx
     651                elif direction==MOVERESIZE_SIZE_TOPLEFT:
     652                    wy = max(0, owy + dy)
     653                    wh = (owy + owh) - wy
     654                    wx = max(0, owx - dx)
     655                    ww = (owx + oww) - wx
     656                elif direction==MOVERESIZE_SIZE_LEFT:
     657                    wx = max(0, owx - dx)
     658                    ww = (owx + oww) - wx
     659                elif direction==MOVERESIZE_SIZE_BOTTOMLEFT:
     660                    wx = max(0, owx - dx)
     661                    ww = (owx + oww) - wx
     662                    wh = owh + dy
     663                else:
     664                    raise Exception("invalid direction: %s" % direction)
     665                log.info("%s(%s with %s)=%s", MOVERESIZE_DIRECTION_STRING.get(direction), (owx, owy, oww, owh), (dx, dy), (wx, wy, ww, wh))
     666                #sanitize values:
     667                ww = max(self.geometry_hints.get("min_width", 1), ww)
     668                ww = min(self.geometry_hints.get("max_width", 32768), ww)
     669                wh = max(self.geometry_hints.get("min_height", 1), wh)
     670                wh = min(self.geometry_hints.get("max_height", 32768), wh)
     671                #TODO: base_width, width_inc, etc..
     672                log.info("sanitized=%s", (wx, wy, ww, wh))
     673                if wx==cwx and wy==cwy:
     674                    #no position change
     675                    if ww!=cww or wh!=cwh:
     676                        self.resize(ww, wh)
     677                    else:
     678                        pass    #no change at all!
     679                else:
     680                    self.move_resize(wx, wy, ww, wh)
     681            return None
     682        return event
    583683
     684    def preprocess_button_action(self, button, event, depressed):
     685        #we override here so we can emulate moveresize (release it):
     686        if self._moveresize and depressed:
     687            release_capture(self)
     688            self._moveresize = None
     689        return button, event, depressed
    584690
     691
     692    def initiate_moveresize(self, x_root, y_root, direction, button, source_indication):
     693        statelog.info("initiate_moveresize%s direction=%s", (x_root, y_root, direction, button, source_indication), MOVERESIZE_DIRECTION_STRING.get(direction))
     694        if EMULATE_MOVERESIZE:
     695            #on _unfocus(): release
     696            if direction==MOVERESIZE_SIZE_KEYBOARD:
     697                #TODO: not implemented!
     698                release_capture(self)
     699                return
     700            if direction==MOVERESIZE_CANCEL:
     701                release_capture(self)
     702                return
     703            self._moveresize = (direction, self.get_window().get_pointer()[:2], self.get_position(), self.get_size())
     704            set_capture(self)
     705        elif HAS_X11_BINDINGS:
     706            event_mask = SubstructureNotifyMask | SubstructureRedirectMask
     707            with xsync:
     708                from xpra.gtk_common.gobject_compat import get_xid
     709                root = self.get_window().get_screen().get_root_window()
     710                root_xid = get_xid(root)
     711                xwin = get_xid(self.get_window())
     712                X11Core.UngrabPointer()
     713                X11Window.sendClientMessage(root_xid, xwin, False, event_mask, "_NET_WM_MOVERESIZE",
     714                      x_root, y_root, direction, button, source_indication)
     715        else:
     716            log.warn("cannot handle initiate-moveresize (no X11 bindings, no emulation)")
     717
     718
    585719    def apply_transient_for(self, wid):
    586720        if wid==-1:
    587721            #root is a gdk window, so we need to ensure we have one
  • xpra/platform/gui.py

     
    119119def set_shaded(window, shaded):
    120120    pass
    121121
     122#for win32 mouse capture:
     123def set_capture(window):
     124    return
    122125
     126def release_capture(window):
     127    return
     128
     129
    123130def gl_check():
    124131    return None     #no problem
    125132
     
    180187                "get_double_click_time", "get_double_click_distance",
    181188                "get_fixed_cursor_size", "get_window_frame_sizes",
    182189                "add_window_hooks", "remove_window_hooks",
     190                "set_capture", "release_capture",
    183191                "system_bell",
    184192                "get_info")
    185193
  • xpra/platform/win32/gui.py

     
    129129    return _propsys
    130130
    131131
     132def set_capture(window):
     133    try:
     134        win32api.SetCapture(window.get_window().handle)
     135    except Exception as e:
     136        log.warn("failed to SetCapture: %s", e)
     137def release_capture(window):
     138    try:
     139        win32api.ReleaseCapture()
     140    except Exception as e:
     141        log.warn("failed to ReleaseCapture: %s", e)
     142
    132143def add_window_hooks(window):
    133144    if not WINDOW_HOOKS:
    134145        #allows us to disable the win32 hooks for testing