xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #543: workspace-change-batch-delay.patch

File workspace-change-batch-delay.patch, 16.8 KB (added by Antoine Martin, 8 years ago)

when the window is not on the current workspace, limit its update speed

  • xpra/client/gtk2/gtk2_window_base.py

     
    130130            log.error("xget_u32_property error on %s / %s: %s", target, name, e)
    131131        return GTKClientWindowBase.xget_u32_property(self, target, name)
    132132
    133     def get_current_workspace(self):
     133    def get_desktop_workspace(self):
    134134        window = self.gdk_window()
    135135        root = window.get_screen().get_root_window()
    136136        return self.do_get_workspace(root, "_NET_CURRENT_DESKTOP")
  • xpra/client/gtk_base/gtk_client_window_base.py

     
    4242            root = gtk.gdk.get_default_root_window()
    4343            supported = prop_get(root, "_NET_SUPPORTED", ["atom"], ignore_errors=True)
    4444            CAN_SET_WORKSPACE = bool(supported) and "_NET_WM_DESKTOP" in supported
    45         except:
    46             pass
     45        except Exception, e:
     46            log.info("failed to setup workspace hooks: %s", e)
    4747    except ImportError, e:
    4848        pass
    4949
     
    6565        self._fullscreen = None
    6666        self._iconified = False
    6767        self._resize_counter = 0
    68         self._current_workspace = self._client_properties.get("workspace")
     68        self._window_workspace = self._client_properties.get("workspace")
     69        self._desktop_workspace = -1
    6970        ClientWindowBase.init_window(self, metadata)
    7071        self._can_set_workspace = HAS_X11_BINDINGS and CAN_SET_WORKSPACE
    7172
     
    164165    def property_changed(self, widget, event):
    165166        log("%s.property_changed(%s, %s) : %s", self, widget, event, event.atom)
    166167        if event.atom=="_NET_WM_DESKTOP" and self._been_mapped and not self._override_redirect:
    167             #fake a configure event to send the new client_properties with
    168             #the updated workspace number:
    169             workspacelog("_NET_WM_DESKTOP modified, faking configure event")
     168            self.do_workspace_changed(event)
     169
     170    def workspace_changed(self):
     171        #on X11 clients, this fires from the root window property watcher
     172        ClientWindowBase.workspace_changed(self)
     173        self.do_workspace_changed("desktop workspace changed")
     174   
     175    def do_workspace_changed(self, info):
     176        #call this method whenever something workspace related may have changed
     177        window_workspace = self.get_window_workspace()
     178        desktop_workspace = self.get_desktop_workspace()
     179        workspacelog("do_worskpace_changed(%s) window/desktop: from %s to %s", info, (self._window_workspace, self._desktop_workspace), (window_workspace, desktop_workspace))
     180        if self._window_workspace==window_workspace and self._desktop_workspace==desktop_workspace:
     181            #no change
     182            return
     183        if desktop_workspace<0 or window_workspace<0:
     184            #not sure about which workspace we are on!?
     185            #maybe the property has been cleared? maybe the window is being scrubbed? do nothing.
     186            return
     187        if not self._client.window_refresh_config:
     188            workspacelog("sending configure event to update workspace value")
    170189            self.process_configure_event()
     190            return
     191        #we can tell the server using a "buffer-refresh" packet instead
     192        #and also take care of tweaking the batch config
     193        client_properties = {"workspace" : window_workspace}
     194        options = {"refresh-now" : False}               #no need to refresh it
     195        if desktop_workspace!=window_workspace:
     196            options["batch"] = {
     197                                "reset"     : True,
     198                                "delay"     : 1000,
     199                                "locked"    : True,
     200                                "always"    : True,
     201                                }
     202            workspacelog("window is on a different workspace, increasing its batch delay: %s", options)
     203        elif self._window_workspace!=self._desktop_workspace:
     204            options["batch"] = {
     205                                "reset"     : True,
     206                                }
     207            workspacelog("window was on a different workspace, resetting its batch delay: %s", options)
     208        self.send("buffer-refresh", self._id, 0, 100, options, client_properties)
     209        self._window_workspace = window_workspace
     210        self._desktop_workspace = desktop_workspace
    171211
    172212
    173213    def set_workspace(self):
     
    175215            return -1
    176216        root = self.gdk_window().get_screen().get_root_window()
    177217        ndesktops = self.xget_u32_property(root, "_NET_NUMBER_OF_DESKTOPS")
    178         workspacelog("%s.set_workspace() workspace=%s ndesktops=%s", self, self._current_workspace, ndesktops)
     218        workspacelog("%s.set_workspace() workspace=%s ndesktops=%s", self, self._window_workspace, ndesktops)
    179219        if ndesktops is None or ndesktops<=1:
    180220            return  -1
    181         workspace = max(0, min(ndesktops-1, self._current_workspace))
     221        workspace = max(0, min(ndesktops-1, self._window_workspace))
    182222        event_mask = SubstructureNotifyMask | SubstructureRedirectMask
    183223
    184224        def send():
     
    189229        trap.call_synced(send)
    190230        return workspace
    191231
    192     def get_current_workspace(self):
     232    def get_desktop_workspace(self):
    193233        return -1
    194234
    195235    def get_window_workspace(self):
     
    290330            props["screen"] = self.get_screen().get_number()
    291331            workspace = self.get_window_workspace()
    292332            if workspace<0:
    293                 workspace = self.get_current_workspace()
    294         if self._current_workspace!=workspace:
    295             workspacelog("map event: changed workspace from %s to %s", self._current_workspace, workspace)
    296             self._current_workspace = workspace
     333                workspace = self.get_desktop_workspace()
     334        if self._window_workspace!=workspace:
     335            workspacelog("map event: changed workspace from %s to %s", self._window_workspace, workspace)
     336            self._window_workspace = workspace
    297337            props["workspace"] = workspace
    298338        log("map-window for wid=%s with client props=%s", self._id, props)
    299339        self.send("map-window", self._id, x, y, w, h, props)
     
    322362            props["screen"] = self.get_screen().get_number()
    323363            workspace = self.get_window_workspace()
    324364            if workspace<0:
    325                 workspace = self.get_current_workspace()
    326             if self._current_workspace!=workspace:
    327                 workspacelog("configure event: changed workspace from %s to %s", self._current_workspace, workspace)
    328                 self._current_workspace = workspace
     365                workspace = self.get_desktop_workspace()
     366            if self._window_workspace!=workspace:
     367                workspacelog("configure event: changed workspace from %s to %s", self._window_workspace, workspace)
     368                self._window_workspace = workspace
    329369                props["workspace"] = workspace
    330370        packet = ["configure-window", self._id, x, y, w, h, props]
    331371        if self._resize_counter>0:
  • xpra/client/ui_client_base.py

     
    158158        self.toggle_cursors_bell_notify = False
    159159        self.toggle_keyboard_sync = False
    160160        self.window_unmap = False
     161        self.window_refresh_config = False
    161162        self.server_generic_encodings = False
    162163        self.server_encodings = []
    163164        self.server_core_encodings = []
     
    998999
    9991000
    10001001    def send_refresh(self, wid):
    1001         self.send("buffer-refresh", wid, True, 95)
     1002        packet = ["buffer-refresh", wid, True, 100]
     1003        if self.window_refresh_config:
     1004            #explicit refresh (should be assumed True anyway),
     1005            #also force a reset of batch configs:
     1006            packet.append({
     1007                           "refresh-now"    : True,
     1008                           "batch"          : {"reset" : True}
     1009                           })
     1010            packet.append({})   #no client_properties
     1011        self.send(*packet)
    10021012
    10031013    def send_refresh_all(self):
    10041014        log("Automatic refresh for all windows ")
     
    10121022            self.session_name = c.strget("session_name", "")
    10131023        set_application_name(self.session_name or "Xpra")
    10141024        self.window_unmap = c.boolget("window_unmap")
     1025        self.window_refresh_config = c.boolget("window_refresh_config")
    10151026        self.suspend_resume = c.boolget("suspend-resume")
    10161027        self.server_supports_notifications = c.boolget("notifications")
    10171028        self.notifications_enabled = self.server_supports_notifications and self.client_supports_notifications
  • xpra/server/batch_config.py

     
    3939        self.max_delay = self.MAX_DELAY
    4040        self.timeout_delay = self.TIMEOUT_DELAY
    4141        self.delay = self.START_DELAY
     42        self.locked = False                             #to force a specific delay
    4243        self.last_delays = maxdeque(64)                 #the delays we have tried to use (milliseconds)
    4344        self.last_actual_delays = maxdeque(64)          #the delays we actually used (milliseconds)
    4445        self.last_updated = 0
     
    4950
    5051
    5152    def add_stats(self, info, prefix, suffix=""):
     53        info[prefix+"batch.min-delay"] = self.min_delay
     54        info[prefix+"batch.max-delay"] = self.max_delay
     55        info[prefix+"batch.timeout-delay"] = self.timeout_delay
     56        info[prefix+"batch.locked"] = self.locked
    5257        if len(self.last_delays)>0:
    5358            batch_delays = [x for _,x in list(self.last_delays)]
    5459            add_list_stats(info, prefix+"batch.delay"+suffix, batch_delays)
     
    6974        return c
    7075
    7176    def __str__(self):
    72         return  "DamageBatchConfig(wid=%s, always=%s, min=%s, max=%s, current=%s, max events=%s, max pixels=%s, time unit=%s)" % \
    73                 (self.wid, self.always, self.min_delay, self.max_delay, self.delay, self.max_events, self.max_pixels, self.time_unit)
     77        return  "DamageBatchConfig(wid=%s, always=%s, min=%s, max=%s, current=%s, locked=%s, max events=%s, max pixels=%s, time unit=%s)" % \
     78                (self.wid, self.always, self.min_delay, self.max_delay, self.delay, self.locked, self.max_events, self.max_pixels, self.time_unit)
  • xpra/server/server_base.py

     
    586586             "toggle_keyboard_sync"         : True,
    587587             "window_configure"             : True,
    588588             "window_unmap"                 : True,
     589             "window_refresh_config"        : True,
    589590             "xsettings-tuple"              : True,
    590591             "change-quality"               : True,
    591592             "change-min-quality"           : True,
     
    14221423
    14231424
    14241425    def _process_buffer_refresh(self, proto, packet):
    1425         [wid, _, qual] = packet[1:4]
     1426        """ can be used for requesting a refresh, or tuning batch config, or both """
     1427        wid, _, qual = packet[1:4]
     1428        options = typedict({})
     1429        client_properties = {}
     1430        if len(packet)>=6:
     1431            options = typedict(packet[4])
     1432            client_properties = packet[5]
    14261433        if wid==-1:
    14271434            wid_windows = self._id_to_window
    14281435        elif wid in self._id_to_window:
    14291436            wid_windows = {wid : self._id_to_window.get(wid)}
    14301437        else:
     1438            log.warn("invalid window specified for refresh: %s", wid)
    14311439            return
    1432         opts = {"quality" : qual,
    1433                 "override_options" : True}
    1434         log("process_buffer_refresh for windows: %s, with options=%s", wid_windows, opts)
    1435         self.refresh_windows(proto, wid_windows, opts)
     1440        log.info("process_buffer_refresh for windows: %s options=%s, client_properties=%s", wid_windows, options, client_properties)
     1441        batch_props = options.dictget("batch", {})
     1442        if batch_props or client_properties:
     1443            #change batch config and/or client properties
     1444            self.update_batch_config(proto, wid_windows, typedict(batch_props), client_properties)
     1445        #default to True for backwards compatibility:
     1446        if options.get("refresh-now", True):
     1447            refresh_opts = {"quality"           : qual,
     1448                            "override_options"  : True}
     1449            self.refresh_windows(proto, wid_windows, refresh_opts)
    14361450
     1451    def update_batch_config(self, proto, wid_windows, batch_props, client_properties):
     1452        ss = self._server_sources.get(proto)
     1453        if ss is None:
     1454            return
     1455        for wid, window in wid_windows.items():
     1456            if window is None or not window.is_managed():
     1457                continue
     1458            self._set_client_properties(proto, wid, window, client_properties)
     1459            ss.update_batch(wid, window, batch_props)
     1460
    14371461    def refresh_windows(self, proto, wid_windows, opts=None):
    14381462        ss = self._server_sources.get(proto)
    14391463        if ss is None:
  • xpra/server/source.py

     
    14401440        w, h = window.get_dimensions()
    14411441        self.damage(wid, window, 0, 0, w, h, opts)
    14421442
     1443    def update_batch(self, wid, window, batch_props):
     1444        ws = self.window_sources.get(wid)
     1445        if ws:
     1446            if "reset" in batch_props:
     1447                ws.batch_config = self.make_batch_config(wid, window)
     1448            for x in ("always", "locked"):
     1449                if x in batch_props:
     1450                    setattr(ws.batch_config, x, batch_props.boolget(x))
     1451            for x in ("min_delay", "max_delay", "timeout_delay", "delay"):
     1452                if x in batch_props:
     1453                    setattr(ws.batch_config, x, batch_props.intget(x))
     1454            log.info("batch config updated: %s", ws.batch_config)
     1455
    14431456    def set_client_properties(self, wid, window, new_client_properties):
    14441457        ws = self.make_window_source(wid, window)
    14451458        ws.set_client_properties(new_client_properties)
    14461459
     1460    def make_batch_config(self, wid, window):
     1461        batch_config = self.default_batch_config.clone()
     1462        batch_config.wid = wid
     1463        #scale initial delay based on window size
     1464        #(the global value is normalized to 1MPixel)
     1465        #but use sqrt to smooth things and prevent excesses
     1466        #(ie: a 4MPixel window, will start at 2 times the global delay)
     1467        #(ie: a 0.5MPixel window will start at 0.7 times the global delay)
     1468        w, h = window.get_dimensions()
     1469        ratio = float(w*h) / 1000000
     1470        batch_config.delay = self.global_batch_config.delay * sqrt(ratio)
     1471        return batch_config
     1472
    14471473    def make_window_source(self, wid, window):
    14481474        ws = self.window_sources.get(wid)
    14491475        if ws is None:
    1450             batch_config = self.default_batch_config.clone()
    1451             batch_config.wid = wid
    1452             #scale initial delay based on window size
    1453             #(the global value is normalized to 1MPixel)
    1454             #but use sqrt to smooth things and prevent excesses
    1455             #(ie: a 4MPixel window, will start at 2 times the global delay)
    1456             #(ie: a 0.5MPixel window will start at 0.7 times the global delay)
    1457             w, h = window.get_dimensions()
    1458             ratio = float(w*h) / 1000000
    1459             batch_config.delay = self.global_batch_config.delay * sqrt(ratio)
    1460 
     1476            batch_config = self.make_batch_config(wid, window)
    14611477            wclass = WindowSource
    14621478            #don't use video for system trays
    14631479            #or for transparent windows (if the client supports transparency) since video doesn't do alpha (yet?)
  • xpra/server/window_source.py

     
    403403        self.batch_config.add_stats(info, prefix, suffix)
    404404
    405405    def calculate_batch_delay(self, has_focus, other_is_fullscreen, other_is_maximized):
    406         calculate_batch_delay(self.wid, self.window_dimensions, has_focus, other_is_fullscreen, other_is_maximized, self.is_OR, self.soft_expired, self.batch_config, self.global_statistics, self.statistics)
     406        if not self.batch_config.locked:
     407            calculate_batch_delay(self.wid, self.window_dimensions, has_focus, other_is_fullscreen, other_is_maximized, self.is_OR, self.soft_expired, self.batch_config, self.global_statistics, self.statistics)
    407408
    408409    def update_speed(self):
    409410        if self.suspended or self._mmap: