示例#1
0
 def _updateLayout(self):
     self._revision += 1
     msg = QDBusMessage.createSignal("/MenuBar", "com.canonical.dbusmenu",
                                     "LayoutUpdated")
     msg.setArguments([
         QDBusArgument(self._revision, QMetaType.UInt),
         QDBusArgument(DBusMenuAction.Root.value, QMetaType.Int),
     ])
     self.__sessionService.sessionBus.send(msg)
示例#2
0
	def PropertiesChanged(self, interface, prop, values):
		"""Sends PropertiesChanged signal through sessionBus.
		Args:
			interface: interface name
			prop: property name
			values: current property value(s)
		"""
		emptyStringListArg = QDBusArgument()
		emptyStringListArg.add([""], QMetaType.QStringList)
		self.signal.setArguments([interface, {prop: values}, emptyStringListArg])
		QDBusConnection.sessionBus().send(self.signal)
示例#3
0
    def EventGroup(self, msg):
        events = msg.arguments()
        for event in events:
            for itemId, eventId, data, timestamp in event:
                self._eventHandler(itemId, eventId, data, timestamp)

        idErrors = QDBusArgument()
        idErrors.beginArray(QMetaType.Int)
        idErrors.endArray()
        reply = msg.createReply([
            idErrors
        ])
        return self.__sessionService.sessionBus.send(reply)
示例#4
0
    def _dbus_notify(self, task):
        if not app.settings.getbool("frontend", "popnotifications"):
            return

        if "actions" in self._capabilities:
            actions = QDBusArgument(["open", "打开", "openDir", "打开文件夹"],
                                    QMetaType.QStringList)
        else:
            actions = QDBusArgument([], QMetaType.QStringList)

        qdBusMsg = self._interface.call(
            "Notify",
            QDBusArgument("Xware Desktop", QMetaType.QString),  # app_name
            QDBusArgument(0, QMetaType.UInt),  # replace_id
            # app_icon
            QDBusArgument(os.path.join(constants.FRONTEND_DIR, "thunder.ico"),
                          QMetaType.QString),
            QDBusArgument("下载完成", QMetaType.QString),  # summary
            QDBusArgument(task["name"], QMetaType.QString),  # body
            actions,
            {
                "category": "transfer.complete",
            },  # hints
            QDBusArgument(5000, QMetaType.Int),  # timeout
        )

        if qdBusMsg.errorName():
            logging.error("DBus, notifyTask {}: {}".format(
                qdBusMsg.errorName(), qdBusMsg.errorMessage()))
        else:
            # add it to the dict
            self._notifications[qdBusMsg.arguments()[0]] = task
示例#5
0
    def _dbus_notifyCompleted(self, task: "TaskItem"):
        if "actions" in self._capabilities:
            actions = QDBusArgument(["open", "打开", "viewOneFile", "在文件夹中显示"],
                                    QMetaType.QStringList)
        else:
            actions = QDBusArgument([], QMetaType.QStringList)

        qdBusMsg = self._interface.call(
            "Notify",
            QDBusArgument("Xware Desktop", QMetaType.QString),  # app_name
            QDBusArgument(0, QMetaType.UInt),  # replace_id
            QDBusArgument("xware-desktop", QMetaType.QString),  # app_icon
            QDBusArgument("下载完成", QMetaType.QString),  # summary
            QDBusArgument(task.name, QMetaType.QString),  # body
            actions,
            {
                "category": "transfer.complete",
            },  # hints
            QDBusArgument(5000, QMetaType.Int),  # timeout
        )

        if qdBusMsg.errorName():
            logging.error("DBus, notifyTask {}: {}".format(
                qdBusMsg.errorName(), qdBusMsg.errorMessage()))
        else:
            # add it to the dict
            notificationId = qdBusMsg.arguments()[0]
            self._notified[notificationId] = task.id
示例#6
0
def _dbus_notify(title, message, duration=5000):
    from PyQt5.QtDBus import (
        QDBus, QDBusArgument, QDBusConnection, QDBusInterface)
    bus = QDBusConnection.sessionBus()
    if not bus.isConnected():
        raise OSError("Could not connect to DBus")
    interface = QDBusInterface(
        'org.freedesktop.Notifications',
        '/org/freedesktop/Notifications',
        'org.freedesktop.Notifications',
        bus)
    error = interface.lastError()
    if error.type():
        raise RuntimeError("{}; {}".format(error.name(), error.message()))
    # See https://developer.gnome.org/notification-spec/
    # "This allows clients to effectively modify the notification while
    # it's active. A value of value of 0 means that this notification
    # won't replace any existing notifications."
    replaces_id = QVariant(0)
    replaces_id.convert(QVariant.UInt)
    interface.call(
        QDBus.NoBlock,
        'Notify',
        APP_NAME,
        replaces_id,
        resource(settings['application']['tray_icon']),
        title,
        message,
        QDBusArgument([], QMetaType.QStringList),
        {},
        duration)
示例#7
0
 def setVisible(self, visible: bool):
     if visible:
         self._interface.call(
             "RegisterStatusNotifierItem",
             QDBusArgument(self.__sessionService.serviceName,
                           QMetaType.QString),
         )
     else:
         # Maybe try unregistering the whole object?
         raise NotImplementedError(
             "UnregisterStatusNotifierItem method doesn't exist.")
    def GetLayout(self, msg):
        parentId, recursionDepth, propertyNames = msg.arguments()
        if parentId != 0:
            raise NotImplementedError(
                "Don't know when happens when parentId is not 0.")

        exitItem = newDBusMenuItem(DBusMenuAction.Exit,
                                   self._getItemProperty(DBusMenuAction.Exit),
                                   [])

        # toggleMainWinItem = newDBusMenuItem(
        #     DBusMenuAction.ToggleMainWindow,
        #     self._getItemProperty(DBusMenuAction.ToggleMainWindow),
        #     []
        # )

        # settingsItem = newDBusMenuItem(
        #     DBusMenuAction.Settings,
        #     self._getItemProperty(DBusMenuAction.Settings),
        #     []
        # )

        toggleMonitorWinItem = newDBusMenuItem(
            DBusMenuAction.ToggleMonitorWindow,
            self._getItemProperty(DBusMenuAction.ToggleMonitorWindow), [])

        rootItem = newDBusMenuItem(
            DBusMenuAction.Root,
            self._getItemProperty(DBusMenuAction.Root),
            [
                toggleMonitorWinItem,
                exitItem,
            ],
        )

        reply = msg.createReply([
            QDBusArgument(self._revision, QMetaType.UInt),
            QDBusArgument(rootItem, QDBusArgument.Structure),
        ])
        return self.__sessionService.sessionBus.send(reply)
示例#9
0
    def EventGroup(self, msg):
        events = msg.arguments()
        for event in events:
            for itemId, eventId, data, timestamp in event:
                self._eventHandler(itemId, eventId, data, timestamp)

        idErrors = QDBusArgument()
        idErrors.beginArray(QMetaType.Int)
        idErrors.endArray()
        reply = msg.createReply([idErrors])
        return self.__sessionService.sessionBus.send(reply)
示例#10
0
    def GetGroupProperties(self, msg):
        itemIds, propertyNames = msg.arguments()
        result = QDBusArgument()
        result.beginArray(GroupPropertyMetaType)
        for itemId in itemIds:
            result.beginStructure()
            result.add(itemId, QMetaType.Int)
            result.add(self._getItemProperty(itemId), QMetaType.QVariantMap)
            result.endStructure()
        result.endArray()

        reply = msg.createReply([
            result
        ])
        return self.__sessionService.sessionBus.send(reply)
示例#11
0
def newDBusMenuItem(action: DBusMenuAction, properties, children):
    item = QDBusArgument()
    item.beginStructure()
    item.add(action.value, QMetaType.Int)  # id
    item.add(properties, QMetaType.QVariantMap)
    item.add(children, QMetaType.QVariantList)
    item.endStructure()
    return item
示例#12
0
    def _convert_image(self, qimage: QImage) -> Optional[QDBusArgument]:
        """Convert a QImage to the structure DBus expects.

        https://specifications.freedesktop.org/notification-spec/latest/ar01s05.html#icons-and-images-formats
        """
        bits_per_color = 8
        has_alpha = qimage.hasAlphaChannel()
        if has_alpha:
            image_format = QImage.Format_RGBA8888
            channel_count = 4
        else:
            image_format = QImage.Format_RGB888
            channel_count = 3

        qimage.convertTo(image_format)
        bytes_per_line = qimage.bytesPerLine()
        width = qimage.width()
        height = qimage.height()

        image_data = QDBusArgument()
        image_data.beginStructure()
        image_data.add(width)
        image_data.add(height)
        image_data.add(bytes_per_line)
        image_data.add(has_alpha)
        image_data.add(bits_per_color)
        image_data.add(channel_count)

        try:
            size = qimage.sizeInBytes()
        except TypeError:
            # WORKAROUND for
            # https://www.riverbankcomputing.com/pipermail/pyqt/2020-May/042919.html
            # byteCount() is obsolete, but sizeInBytes() is only available with
            # SIP >= 5.3.0.
            size = qimage.byteCount()

        # Despite the spec not mandating this, many notification daemons mandate that
        # the last scanline does not have any padding bytes.
        #
        # Or in the words of dunst:
        #
        #     The image is serialised rowwise pixel by pixel. The rows are aligned by a
        #     spacer full of garbage. The overall data length of data + garbage is
        #     called the rowstride.
        #
        #     Mind the missing spacer at the last row.
        #
        #     len:     |<--------------rowstride---------------->|
        #     len:     |<-width*pixelstride->|
        #     row 1:   |   data for row 1    | spacer of garbage |
        #     row 2:   |   data for row 2    | spacer of garbage |
        #              |         .           | spacer of garbage |
        #              |         .           | spacer of garbage |
        #              |         .           | spacer of garbage |
        #     row n-1: |   data for row n-1  | spacer of garbage |
        #     row n:   |   data for row n    |
        #
        # Source:
        # https://github.com/dunst-project/dunst/blob/v1.6.1/src/icon.c#L292-L309
        padding = bytes_per_line - width * channel_count
        assert 0 <= padding <= 3, (padding, bytes_per_line, width,
                                   channel_count)
        size -= padding

        if padding and self._quirks.no_padded_images:
            return None

        bits = qimage.constBits().asstring(size)
        image_data.add(QByteArray(bits))

        image_data.endStructure()
        return image_data
示例#13
0
 def _get_actions_arg(self) -> QDBusArgument:
     """Get the actions argument for present()."""
     actions = []
     if self._capabilities.actions:
         actions = ['default', 'Activate']  # key, name
     return QDBusArgument(actions, QMetaType.QStringList)
示例#14
0
    def present(
        self,
        qt_notification: "QWebEngineNotification",
        *,
        replaces_id: Optional[int],
    ) -> int:
        """Shows a notification over DBus."""
        if replaces_id is None:
            replaces_id = 0  # 0 is never a valid ID according to the spec

        actions = []
        if self._capabilities.actions:
            actions = ['default', 'Activate']  # key, name
        actions_arg = QDBusArgument(actions, QMetaType.QStringList)

        origin_url_str = qt_notification.origin().toDisplayString()
        hints: Dict[str, Any] = {
            # Include the origin in case the user wants to do different things
            # with different origin's notifications.
            "x-qutebrowser-origin": origin_url_str,
            "desktop-entry": "org.qutebrowser.qutebrowser",
        }

        is_useful_origin = self._should_include_origin(
            qt_notification.origin())
        if self._capabilities.kde_origin_name and is_useful_origin:
            hints["x-kde-origin-name"] = origin_url_str

        icon = qt_notification.icon()
        if icon.isNull():
            filename = ':/icons/qutebrowser-64x64.png'
            icon = QImage(filename)

        key = self._quirks.icon_key or "image-data"
        data = self._convert_image(icon)
        if data is not None:
            hints[key] = data

        # Titles don't support markup (except with broken servers)
        title = qt_notification.title()
        if self._quirks.escape_title:
            title = html.escape(title, quote=False)

        reply = self.interface.call(
            QDBus.BlockWithGui,
            "Notify",
            "qutebrowser",  # application name
            _as_uint32(replaces_id),  # replaces notification id
            "",  # icon name/file URL, we use image-data and friends instead.
            title,
            self._format_body(qt_notification.message(),
                              qt_notification.origin()),
            actions_arg,
            hints,
            -1,  # timeout; -1 means 'use default'
        )
        self._verify_message(reply, "u", QDBusMessage.ReplyMessage)

        notification_id = reply.arguments()[0]

        if replaces_id not in [0, notification_id]:
            msg = (
                f"Wanted to replace notification {replaces_id} but got new id "
                f"{notification_id}.")
            if self._quirks.wrong_replaces_id:
                log.misc.debug(msg)
            else:
                log.misc.error(msg)

        return notification_id
示例#15
0
    def GetGroupProperties(self, msg):
        itemIds, propertyNames = msg.arguments()
        result = QDBusArgument()
        result.beginArray(GroupPropertyMetaType)
        for itemId in itemIds:
            result.beginStructure()
            result.add(itemId, QMetaType.Int)
            result.add(self._getItemProperty(itemId), QMetaType.QVariantMap)
            result.endStructure()
        result.endArray()

        reply = msg.createReply([result])
        return self.__sessionService.sessionBus.send(reply)
示例#16
0
def newDBusMenuItem(action: DBusMenuAction, properties, children):
    item = QDBusArgument()
    item.beginStructure()
    item.add(action.value, QMetaType.Int)  # id
    item.add(properties, QMetaType.QVariantMap)
    item.add(children, QMetaType.QVariantList)
    item.endStructure()
    return item