kylin-wayland-compositor/ 0000775 0001750 0001750 00000000000 15160461067 014465 5 ustar feng feng kylin-wayland-compositor/protocols/ 0000775 0001750 0001750 00000000000 15160461067 016511 5 ustar feng feng kylin-wayland-compositor/protocols/text-input-unstable-v2.xml 0000664 0001750 0001750 00000047362 15160460057 023526 0 ustar feng feng
Copyright © 2012, 2013 Intel Corporation
Copyright © 2015, 2016 Jan Arne Petersen
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, 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.
The zwp_text_input_v2 interface represents text input and input methods
associated with a seat. It provides enter/leave events to follow the
text input focus for a seat.
Requests are used to enable/disable the text-input object and set
state information like surrounding and selected text or the content type.
The information about the entered text is sent to the text-input object
via the pre-edit and commit events. Using this interface removes the need
for applications to directly process hardware key events and compose text
out of them.
Text is valid UTF-8 encoded, indices and lengths are in bytes. Indices
have to always point to the first byte of an UTF-8 encoded code point.
Lengths are not allowed to contain just a part of an UTF-8 encoded code
point.
State is sent by the state requests (set_surrounding_text,
set_content_type, set_cursor_rectangle and set_preferred_language) and
an update_state request. After an enter or an input_method_change event
all state information is invalidated and needs to be resent from the
client. A reset or entering a new widget on client side also
invalidates all current state information.
Destroy the wp_text_input object. Also disables all surfaces enabled
through this wp_text_input object
Enable text input in a surface (usually when a text entry inside of it
has focus).
This can be called before or after a surface gets text (or keyboard)
focus via the enter event. Text input to a surface is only active
when it has the current text (or keyboard) focus and is enabled.
Disable text input in a surface (typically when there is no focus on any
text entry inside the surface).
Requests input panels (virtual keyboard) to show.
This should be used for example to show a virtual keyboard again
(with a tap) after it was closed by pressing on a close button on the
keyboard.
Requests input panels (virtual keyboard) to hide.
Sets the plain surrounding text around the input position. Text is
UTF-8 encoded. Cursor is the byte offset within the surrounding text.
Anchor is the byte offset of the selection anchor within the
surrounding text. If there is no selected text, anchor is the same as
cursor.
Make sure to always send some text before and after the cursor
except when the cursor is at the beginning or end of text.
When there was a configure_surrounding_text event take the
before_cursor and after_cursor arguments into account for picking how
much surrounding text to send.
There is a maximum length of wayland messages so text can not be
longer than 4000 bytes.
Content hint is a bitmask to allow to modify the behavior of the text
input.
The content purpose allows to specify the primary purpose of a text
input.
This allows an input method to show special purpose input panels with
extra characters or to disallow some characters.
Sets the content purpose and content hint. While the purpose is the
basic purpose of an input field, the hint flags allow to modify some
of the behavior.
When no content type is explicitly set, a normal content purpose with
none hint should be assumed.
Sets the cursor outline as a x, y, width, height rectangle in surface
local coordinates.
Allows the compositor to put a window with word suggestions near the
cursor.
Sets a specific language. This allows for example a virtual keyboard to
show a language specific layout. The "language" argument is a RFC-3066
format language tag.
It could be used for example in a word processor to indicate language of
currently edited document or in an instant message application which
tracks languages of contacts.
Defines the reason for sending an updated state.
Allows to atomically send state updates from client.
This request should follow after a batch of state updating requests
like set_surrounding_text, set_content_type, set_cursor_rectangle and
set_preferred_language.
The flags field indicates why an updated state is sent to the input
method.
Reset should be used by an editor widget after the text was changed
outside of the normal input method flow.
For "change" it is enough to send the changed state, else the full
state should be send.
Serial should be set to the serial from the last enter or
input_method_changed event.
To make sure to not receive outdated input method events after a
reset or switching to a new widget wl_display_sync() should be used
after update_state in these cases.
Notification that this seat's text-input focus is on a certain surface.
When the seat has the keyboard capability the text-input focus follows
the keyboard focus.
Notification that this seat's text-input focus is no longer on
a certain surface.
The leave notification is sent before the enter notification
for the new focus.
When the seat has the keyboard capability the text-input focus follows
the keyboard focus.
Notification that the visibility of the input panel (virtual keyboard)
changed.
The rectangle x, y, width, height defines the area overlapped by the
input panel (virtual keyboard) on the surface having the text
focus in surface local coordinates.
That can be used to make sure widgets are visible and not covered by
a virtual keyboard.
Notify when a new composing text (pre-edit) should be set around the
current cursor position. Any previously set composing text should
be removed.
The commit text can be used to replace the composing text in some cases
(for example when losing focus).
The text input should also handle all preedit_style and preedit_cursor
events occurring directly before preedit_string.
Sets styling information on composing text. The style is applied for
length bytes from index relative to the beginning of the composing
text (as byte offset). Multiple styles can be applied to a composing
text by sending multiple preedit_styling events.
This event is handled as part of a following preedit_string event.
Sets the cursor position inside the composing text (as byte
offset) relative to the start of the composing text. When index is a
negative number no cursor is shown.
When no preedit_cursor event is sent the cursor will be at the end of
the composing text by default.
This event is handled as part of a following preedit_string event.
Notify when text should be inserted into the editor widget. The text to
commit could be either just a single character after a key press or the
result of some composing (pre-edit). It could be also an empty text
when some text should be removed (see delete_surrounding_text) or when
the input cursor should be moved (see cursor_position).
Any previously set composing text should be removed.
Notify when the cursor or anchor position should be modified.
This event should be handled as part of a following commit_string
event.
The text between anchor and index should be selected.
Notify when the text around the current cursor position should be
deleted. BeforeLength and afterLength is the length (in bytes) of text
before and after the current cursor position (excluding the selection)
to delete.
This event should be handled as part of a following commit_string
or preedit_string event.
Transfer an array of 0-terminated modifiers names. The position in
the array is the index of the modifier as used in the modifiers
bitmask in the keysym event.
Notify when a key event was sent. Key events should not be used
for normal text input operations, which should be done with
commit_string, delete_surrounding_text, etc. The key event follows
the wl_keyboard key event convention. Sym is a XKB keysym, state a
wl_keyboard key_state. Modifiers are a mask for effective modifiers
(where the modifier indices are set by the modifiers_map event)
Sets the language of the input text. The "language" argument is a RFC-3066
format language tag.
Sets the text direction of input text.
It is mainly needed for showing input cursor on correct side of the
editor when there is no input yet done and making sure neutral
direction text is laid out properly.
Configure what amount of surrounding text is expected by the
input method. The surrounding text will be sent in the
set_surrounding_text request on the following state information updates.
The input method changed on compositor side, which invalidates all
current state information. New state information should be sent from
the client via state requests (set_surrounding_text,
set_content_hint, ...) and update_state.
A factory for text-input objects. This object is a global singleton.
Destroy the wp_text_input_manager object.
Creates a new text-input object for a given seat.
kylin-wayland-compositor/protocols/kywc-output-v1.xml 0000664 0001750 0001750 00000024612 15160460057 022075 0 ustar feng feng
This interface is a manager that allows reading and writing the current
output device configuration.
This event introduces a new output. This happens whenever a new output
appears (e.g. a monitor is plugged in) or after the output manager is bound.
This events is sent when the output will become the primary output.
This event is sent after all information has been sent after binding to
the output manager object and after any subsequent changes. This applies
to child output and mode objects as well.
This allows changes to the output configuration to be seen as atomic,
even if they happen via multiple events.
Indicates the client no longer wishes to receive events for output
configuration changes. However the compositor may emit further events,
until the finished event is emitted.
The client must not send any more requests after this one.
This event indicates that the compositor is done sending manager events.
The compositor will destroy the object immediately after sending this
event, so it will become invalid and the client should release any
resources associated with it.
A output has some read-only properties: modes, name, description,
physical_size. These cannot be changed by clients.
Properties sent via this interface are applied atomically via the
kywc_output_manager.done event. No guarantees are made regarding the order
in which properties are sent.
This event describes the output name.
The name event is sent after a kywc_output object is created. This
event is only sent once per object, and the name does not change over
the lifetime of the kywc_output object.
This event describes the manufacturer of the output monitor.
This event describes the model of the output monitor.
This event describes the serial number of the output monitor.
This event describes a human-readable description of the output.
This event describes the physical size of the monitor. This event is only
sent if has a physical size (e.g. is not a projector or a virtual device).
This event introduces a mode for this monitor. It is sent once per supported mode.
Describes what capabilities this device has.
What capabilities this output has, sent on startup before the first
done event.
This event describes whether the output is enabled. A disabled output is not
mapped to a region of the global compositor space.
When a output is disabled, some properties (current_mode, position,
transform and scale) are irrelevant.
This event describes the mode currently in use for this output. It is only
sent if the output is enabled.
This events describes the position of the output in the global compositor
space. It is only sent if the output is enabled.
This event describes the transformation currently applied to the output.
It is only sent if the output is enabled.
This events describes the scale of the output in the global compositor
space. It is only sent if the output is enabled.
This events describes the power(dpms) state.
This events describes the output brightness.
This events describes the output color temperature.
This event indicates that the output is no longer available. The output
object becomes inert. Clients should send a destroy request and release
any resources associated with it.
This request indicates that the client will no longer use this output object.
This object describes an output mode.
Properties sent via this interface are applied atomically via the
kywc_output_manager.done event. No guarantees are made regarding the order
in which properties are sent.
This event describes the mode size. The size is given in physical
hardware units of the output device. This is not necessarily the same as
the output size in the global compositor space. For instance, the output
may be scaled or transformed.
This event describes the mode's fixed vertical refresh rate. It is only
sent if the mode has a fixed refresh rate.
This event advertises this mode as preferred.
This event indicates that the mode is no longer available. The mode
object becomes inert. Clients should send a destroy request and release
any resources associated with it.
This request indicates that the client will no longer use this mode
object.
kylin-wayland-compositor/protocols/xdg-toplevel-icon-v1.xml 0000664 0001750 0001750 00000022537 15160461067 023130 0 ustar feng feng
Copyright © 2023-2024 Matthias Klumpp
Copyright © 2024 David Edmundson
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
This protocol allows clients to set icons for their toplevel surfaces
either via the XDG icon stock (using an icon name), or from pixel data.
A toplevel icon represents the individual toplevel (unlike the application
or launcher icon, which represents the application as a whole), and may be
shown in window switchers, window overviews and taskbars that list
individual windows.
This document adheres to RFC 2119 when using words like "must",
"should", "may", etc.
Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
This interface allows clients to create toplevel window icons and set
them on toplevel windows to be displayed to the user.
Destroy the toplevel icon manager.
This does not destroy objects created with the manager.
Creates a new icon object. This icon can then be attached to a
xdg_toplevel via the 'set_icon' request.
This request assigns the icon 'icon' to 'toplevel', or clears the
toplevel icon if 'icon' was null.
This state is double-buffered and is applied on the next
wl_surface.commit of the toplevel.
After making this call, the xdg_toplevel_icon_v1 provided as 'icon'
can be destroyed by the client without 'toplevel' losing its icon.
The xdg_toplevel_icon_v1 is immutable from this point, and any
future attempts to change it must raise the
'xdg_toplevel_icon_v1.immutable' protocol error.
The compositor must set the toplevel icon from either the pixel data
the icon provides, or by loading a stock icon using the icon name.
See the description of 'xdg_toplevel_icon_v1' for details.
If 'icon' is set to null, the icon of the respective toplevel is reset
to its default icon (usually the icon of the application, derived from
its desktop-entry file, or a placeholder icon).
If this request is passed an icon with no pixel buffers or icon name
assigned, the icon must be reset just like if 'icon' was null.
This event indicates an icon size the compositor prefers to be
available if the client has scalable icons and can render to any size.
When the 'xdg_toplevel_icon_manager_v1' object is created, the
compositor may send one or more 'icon_size' events to describe the list
of preferred icon sizes. If the compositor has no size preference, it
may not send any 'icon_size' event, and it is up to the client to
decide a suitable icon size.
A sequence of 'icon_size' events must be finished with a 'done' event.
If the compositor has no size preferences, it must still send the
'done' event, without any preceding 'icon_size' events.
This event is sent after all 'icon_size' events have been sent.
This interface defines a toplevel icon.
An icon can have a name, and multiple buffers.
In order to be applied, the icon must have either a name, or at least
one buffer assigned. Applying an empty icon (with no buffer or name) to
a toplevel should reset its icon to the default icon.
It is up to compositor policy whether to prefer using a buffer or loading
an icon via its name. See 'set_name' and 'add_buffer' for details.
Destroys the 'xdg_toplevel_icon_v1' object.
The icon must still remain set on every toplevel it was assigned to,
until the toplevel icon is reset explicitly.
This request assigns an icon name to this icon.
Any previously set name is overridden.
The compositor must resolve 'icon_name' according to the lookup rules
described in the XDG icon theme specification[1] using the
environment's current icon theme.
If the compositor does not support icon names or cannot resolve
'icon_name' according to the XDG icon theme specification it must
fall back to using pixel buffer data instead.
If this request is made after the icon has been assigned to a toplevel
via 'set_icon', a 'immutable' error must be raised.
[1]: https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
This request adds pixel data supplied as wl_buffer to the icon.
The client should add pixel data for all icon sizes and scales that
it can provide, or which are explicitly requested by the compositor
via 'icon_size' events on xdg_toplevel_icon_manager_v1.
The wl_buffer supplying pixel data as 'buffer' must be backed by wl_shm
and must be a square (width and height being equal).
If any of these buffer requirements are not fulfilled, a 'invalid_buffer'
error must be raised.
If this icon instance already has a buffer of the same size and scale
from a previous 'add_buffer' request, data from the last request
overrides the preexisting pixel data.
The wl_buffer must be kept alive for as long as the xdg_toplevel_icon
it is associated with is not destroyed, otherwise a 'no_buffer' error
is raised. The buffer contents must not be modified after it was
assigned to the icon. As a result, the region of the wl_shm_pool's
backing storage used for the wl_buffer must not be modified after this
request is sent. The wl_buffer.release event is unused.
If this request is made after the icon has been assigned to a toplevel
via 'set_icon', a 'immutable' error must be raised.
kylin-wayland-compositor/protocols/meson.build 0000664 0001750 0001750 00000011626 15160461067 020661 0 ustar feng feng wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
wayland_scanner = find_program('wayland-scanner')
protocols = {
'wl-drm': 'drm.xml',
'ext-idle-notifier-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml',
'xdg-shell': wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
'xdg-output-unstable-v1': wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
'tablet-v2': wl_protocol_dir / 'stable/tablet/tablet-v2.xml',
'zwp-text-input-v1': wl_protocol_dir / 'unstable/text-input/text-input-unstable-v1.xml',
'zwp-text-input-v2': 'text-input-unstable-v2.xml',
'xdg-decoration': wl_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml',
'xdg-dialog': wl_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml',
'org-kde-kwin-idle': 'idle.xml',
'zwp-pointer-constraints-v1': wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
'ext-transient-seat-v1': wl_protocol_dir / 'staging/ext-transient-seat/ext-transient-seat-v1.xml',
'xdg-toplevel-drag-manager-v1': wl_protocol_dir / 'staging/xdg-toplevel-drag/xdg-toplevel-drag-v1.xml',
'wp-tearing-control-manager-v1': wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
'wp-cursor-shape-manager-v1': wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
'single-pixel-buffer-v1': wl_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml',
}
protocols += {
'kywc-capture-manager-v1': 'kywc-capture-v1.xml',
'kywc-output-manager-v1': 'kywc-output-v1.xml',
'kywc-toplevel-manager-v1': 'kywc-toplevel-v1.xml',
'kywc-workspace-manager-v1': 'kywc-workspace-v1.xml',
'wlr-screencopy-unstable-v1': 'wlr-screencopy-unstable-v1.xml',
}
if have_drm_lease_device
protocols += {
'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
}
endif
if have_alpha_modifier
protocols += {
'alpha-modifier-v1': wl_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml',
}
endif
if have_kde_output
protocols += {
'kde-output-device-v2': 'kde-output-device-v2.xml',
'kde-output-management-v2' : 'kde-output-management-v2.xml',
'kde-primary-output-v1': 'kde-primary-output-v1.xml',
'org-kde-kwin-dpms-manager': 'dpms.xml',
}
endif
if have_kde_virtual_desktop
protocols += {
'org-kde-plasma-virtual-desktop-management': 'plasma-virtual-desktop.xml',
}
endif
if have_wlr_output
protocols += {
'zwlr-output-power-manager-v1': 'wlr-output-power-management-unstable-v1.xml',
}
endif
if have_wlr_layer_shell
protocols += {
'zwlr-layer-shell-v1': 'wlr-layer-shell-unstable-v1.xml',
}
endif
if have_kde_plasma_shell
protocols += {
'org-kde-plasma-shell': 'plasma-shell.xml',
}
endif
if have_kde_plasma_window_management
protocols += {
'org-kde-plasma-window-management': 'plasma-window-management.xml',
}
endif
if have_kde_blur
protocols += {
'org-kde-kwin-blur-manager': 'blur.xml',
}
endif
if have_kde_slide
protocols += {
'org-kde-kwin-slide-manager': 'slide.xml',
}
endif
if have_kde_keystate
protocols += {
'org-kde-kwin-keystate-manager': 'kde-keystate.xml',
}
endif
if have_ukui_shell
protocols += {
'ukui-shell-v1': 'ukui-shell-v1.xml',
}
endif
if have_ukui_window_management
protocols += {
'ukui-window-management': 'ukui-window-management.xml',
}
endif
if have_ukui_output
protocols += {
'ukui-output-v1': 'ukui-output-v1.xml',
}
endif
if have_ukui_blur
protocols += {
'ukui-blur': 'ukui-blur-v1.xml',
}
endif
if have_ukui_effect
protocols += {
'ukui-effect-v1': 'ukui-effect-v1.xml',
}
endif
if have_ukui_startup
protocols += {
'ukui-startup-v2': 'ukui-startup-v2.xml',
}
endif
if have_xdg_toplevel_icon
protocols += {
'xdg-toplevel-icon-v1': 'xdg-toplevel-icon-v1.xml',
}
endif
if have_tools
protocols += {
'virtual-keyboard-unstable-v1': 'virtual-keyboard-unstable-v1.xml',
'wlr-virtual-pointer-unstable-v1': 'wlr-virtual-pointer-unstable-v1.xml',
}
endif
protocols_code = {}
protocols_server_header = {}
protocols_client_header = {}
foreach name, path : protocols
code = custom_target(
name.underscorify() + '_c',
input: path,
output: '@BASENAME@-protocol.c',
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
)
wlcom_sources += code
server_header = custom_target(
name.underscorify() + '_server_h',
input: path,
output: '@BASENAME@-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
)
wlcom_sources += server_header
client_header = custom_target(
name.underscorify() + '_client_h',
input: path,
output: '@BASENAME@-client-protocol.h',
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
build_by_default: false,
)
protocols_code += { name: code }
protocols_server_header += { name: server_header }
protocols_client_header += { name: client_header }
endforeach
kylin-wayland-compositor/protocols/kde-output-device-v2.xml 0000664 0001750 0001750 00000042606 15160460057 023124 0 ustar feng feng
SPDX-FileCopyrightText: 2021 Méven Car
SPDX-License-Identifier: Expat-CMU
]]>
An output device describes a display device available to the compositor.
output_device is similar to wl_output, but focuses on output
configuration management.
A client can query all global output_device objects to enlist all
available display devices, even those that may currently not be
represented by the compositor as a wl_output.
The client sends configuration changes to the server through the
outputconfiguration interface, and the server applies the configuration
changes to the hardware and signals changes to the output devices
accordingly.
This object is published as global during start up for every available
display devices, or when one later becomes available, for example by
being hotplugged via a physical connector.
This enumeration describes how the physical pixels on an output are
laid out.
This describes the transform, that a compositor will apply to a
surface to compensate for the rotation or mirroring of an
output device.
The flipped values correspond to an initial flip around a
vertical axis followed by rotation.
The purpose is mainly to allow clients to render accordingly and
tell the compositor, so that for fullscreen surfaces, the
compositor is still able to scan out directly client surfaces.
The geometry event describes geometric properties of the output.
The event is sent when binding to the output object and whenever
any of the properties change.
This event describes the mode currently in use for this head. It is only
sent if the output is enabled.
The mode event describes an available mode for the output.
When the client binds to the output_device object, the server sends this
event once for every available mode the output_device can be operated by.
There will always be at least one event sent out on initial binding,
which represents the current mode.
Later if an output changes, its mode event is sent again for the
eventual added modes and lastly the current mode. In other words, the
current mode is always represented by the latest event sent with the current
flag set.
The size of a mode is given in physical hardware units of the output device.
This is not necessarily the same as the output size in the global compositor
space. For instance, the output may be scaled, as described in
kde_output_device_v2.scale, or transformed, as described in
kde_output_device_v2.transform.
This event is sent after all other properties have been
sent on binding to the output object as well as after any
other output property change have been applied later on.
This allows to see changes to the output properties as atomic,
even if multiple events successively announce them.
This event contains scaling geometry information
that is not in the geometry event. It may be sent after
binding the output object or if the output scale changes
later. If it is not sent, the client should assume a
scale of 1.
A scale larger than 1 means that the compositor will
automatically scale surface buffers by this amount
when rendering. This is used for high resolution
displays where applications rendering at the native
resolution would be too small to be legible.
It is intended that scaling aware clients track the
current output of a surface, and if it is on a scaled
output it should use wl_surface.set_buffer_scale with
the scale of the output. That way the compositor can
avoid scaling the surface, and the client can supply
a higher detail image.
The edid event encapsulates the EDID data for the outputdevice.
The event is sent when binding to the output object. The EDID
data may be empty, in which case this event is sent anyway.
If the EDID information is empty, you can fall back to the name
et al. properties of the outputdevice.
The enabled event notifies whether this output is currently
enabled and used for displaying content by the server.
The event is sent when binding to the output object and
whenever later on an output changes its state by becoming
enabled or disabled.
The uuid can be used to identify the output. It's controlled by
the server entirely. The server should make sure the uuid is
persistent across restarts. An empty uuid is considered invalid.
Serial ID of the monitor, sent on startup before the first done event.
EISA ID of the monitor, sent on startup before the first done event.
Describes what capabilities this device has.
What capabilities this device has, sent on startup before the first
done event.
Overscan value of the monitor in percent, sent on startup before the
first done event.
Describes when the compositor may employ variable refresh rate
What policy the compositor will employ regarding its use of variable
refresh rate.
Whether full or limited color range should be used
What rgb range the compositor is using for this output
Name of the output, it's useful to cross-reference to an zxdg_output_v1 and ultimately QScreen
Whether or not high dynamic range is enabled for this output
If high dynamic range is used, this value defines the brightness in nits for content
that's in standard dynamic range format. Note that while the value is in nits, that
doesn't necessarily translate to the same brightness on the screen.
Whether or not the use of a wide color gamut is enabled for this output
This can be used to provide the colors users assume sRGB applications should have based on the
default experience on many modern sRGB screens.
This is the brightness modifier of the output. It doesn't specify
any absolute values, but is merely a multiplier on top of other
brightness values, like sdr_brightness and brightness_metadata.
0 is the minimum brightness (not completely dark) and 10000 is
the maximum brightness.
This is currently only supported / meaningful while HDR is active.
This object describes an output mode.
Some heads don't support output modes, in which case modes won't be
advertised.
Properties sent via this interface are applied atomically via the
kde_output_device.done event. No guarantees are made regarding the order
in which properties are sent.
This event describes the mode size. The size is given in physical
hardware units of the output device. This is not necessarily the same as
the output size in the global compositor space. For instance, the output
may be scaled or transformed.
This event describes the mode's fixed vertical refresh rate. It is only
sent if the mode has a fixed refresh rate.
This event advertises this mode as preferred.
The compositor will destroy the object immediately after sending this
event, so it will become invalid and the client should release any
resources associated with it.
kylin-wayland-compositor/protocols/ukui-output-v1.xml 0000664 0001750 0001750 00000005371 15160460057 022076 0 ustar feng feng
This interface is a manager that allows show some additional output info.
This event introduces a new output. This happens whenever a new output
appears (e.g. a monitor is plugged in) or after the ukui_output_management_v1
is bound.
This event is sent after all information has been sent after binding to
the output manager object and after any subsequent changes. This applies
to child output as well.
A output has some read-only properties: name, usable_area.
These cannot be changed by clients.
This event describes the name of output.
The name event is sent after a ukui_output_v1 object is created. This
event is only sent once per object, and the name does not change over
the lifetime of the ukui_output_v1 object.
This event describes the usable_area of output.
This event indicates that the ukui_output_v1 is no longer available.
Clients should send a destroy request and release any resources associated
with it.
kylin-wayland-compositor/protocols/kde-output-management-v2.xml 0000664 0001750 0001750 00000034301 15160460057 023772 0 ustar feng feng
SPDX-FileCopyrightText: 2021 Méven Car
SPDX-FileCopyrightText: 2023 Xaver Hugl
SPDX-License-Identifier: Expat-CMU
]]>
This interface enables clients to set properties of output devices for screen
configuration purposes via the server. To this end output devices are referenced
by global kde_output_device_v2 objects.
outputmanagement (wl_global)
--------------------------
request:
* create_configuration -> outputconfiguration (wl_resource)
outputconfiguration (wl_resource)
--------------------------
requests:
* enable(outputdevice, bool)
* mode(outputdevice, mode)
* transformation(outputdevice, flag)
* position(outputdevice, x, y)
* apply
events:
* applied
* failed
The server registers one outputmanagement object as a global object. In order
to configure outputs a client requests create_configuration, which provides a
resource referencing an outputconfiguration for one-time configuration. That
way the server knows which requests belong together and can group them by that.
On the outputconfiguration object the client calls for each output whether the
output should be enabled, which mode should be set (by referencing the mode from
the list of announced modes) and the output's global position. Once all outputs
are configured that way, the client calls apply.
At that point and not earlier the server should try to apply the configuration.
If this succeeds the server emits the applied signal, otherwise the failed
signal, such that the configuring client is noticed about the success of its
configuration request.
Through this design the interface enables atomic output configuration changes if
internally supported by the server.
Request an outputconfiguration object through which the client can configure
output devices.
outputconfiguration is a client-specific resource that can be used to ask
the server to apply changes to available output devices.
The client receives a list of output devices from the registry. When it wants
to apply new settings, it creates a configuration object from the
outputmanagement global, writes changes through this object's enable, scale,
transform and mode calls. It then asks the server to apply these settings in
an atomic fashion, for example through Linux' DRM interface.
The server signals back whether the new settings have applied successfully
or failed to apply. outputdevice objects are updated after the changes have been
applied to the hardware and before the server side sends the applied event.
These error can be emitted in response to kde_output_configuration_v2 requests.
Mark the output as enabled or disabled.
Sets the mode for a given output.
Sets the transformation for a given output.
Sets the position for this output device. (x,y) describe the top-left corner
of the output in global space, whereby the origin (0,0) of the global space
has to be aligned with the top-left corner of the most left and in case this
does not define a single one the top output.
There may be no gaps or overlaps between outputs, i.e. the outputs are
stacked horizontally, vertically, or both on each other.
Sets the scaling factor for this output device.
Asks the server to apply property changes requested through this outputconfiguration
object to all outputs on the server side.
The output configuration can be applied only once. The already_applied protocol error
will be posted if the apply request is called the second time.
Sent after the server has successfully applied the changes.
.
Sent if the server rejects the changes or failed to apply them.
Set the overscan value of this output device with a value in percent.
Describes when the compositor may employ variable refresh rate
Set what policy the compositor should employ regarding its use of
variable refresh rate.
Whether this output should use full or limited rgb.
Whether full or limited color range should be used
The order of outputs can be used to assign desktop environment components to a specific screen,
see kde_output_order_v1 for details. The priority is 1-based for outputs that will be enabled after
this changeset is applied, all outputs that are disabled need to have the index set to zero.
Sets whether or not the output should be set to HDR mode.
Sets the brightness of standard dynamic range content in nits. Only has an effect while the output is in HDR mode.
Note that while the value is in nits, that doesn't necessarily translate to the same brightness on the screen.
Whether or not the output should use a wide color gamut
This can be used to provide the colors users assume sRGB applications should have based on the
default experience on many modern sRGB screens.
Set the brightness modifier of the output. It doesn't specify
any absolute values, but is merely a multiplier on top of other
brightness values, like sdr_brightness and brightness_metadata.
0 is the minimum brightness (not completely dark) and 10000 is
the maximum brightness.
This is currently only supported / meaningful while HDR is active.
kylin-wayland-compositor/protocols/virtual-keyboard-unstable-v1.xml 0000664 0001750 0001750 00000011426 15160460057 024660 0 ustar feng feng
Copyright © 2008-2011 Kristian Høgsberg
Copyright © 2010-2013 Intel Corporation
Copyright © 2012-2013 Collabora, Ltd.
Copyright © 2018 Purism SPC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
The virtual keyboard provides an application with requests which emulate
the behaviour of a physical keyboard.
This interface can be used by clients on its own to provide raw input
events, or it can accompany the input method protocol.
Provide a file descriptor to the compositor which can be
memory-mapped to provide a keyboard mapping description.
Format carries a value from the keymap_format enumeration.
A key was pressed or released.
The time argument is a timestamp with millisecond granularity, with an
undefined base. All requests regarding a single object must share the
same clock.
Keymap must be set before issuing this request.
State carries a value from the key_state enumeration.
Notifies the compositor that the modifier and/or group state has
changed, and it should update state.
The client should use wl_keyboard.modifiers event to synchronize its
internal state with seat state.
Keymap must be set before issuing this request.
A virtual keyboard manager allows an application to provide keyboard
input events as if they came from a physical keyboard.
Creates a new virtual keyboard associated to a seat.
If the compositor enables a keyboard to perform arbitrary actions, it
should present an error when an untrusted client requests a new
keyboard.
kylin-wayland-compositor/protocols/plasma-window-management.xml 0000664 0001750 0001750 00000046143 15160460057 024135 0 ustar feng feng
This interface manages application windows.
It provides requests to show and hide the desktop and emits
an event every time a window is created so that the client can
use it to manage the window.
Only one client can bind this interface at a time.
Tell the compositor to show/hide the desktop.
Deprecated: use get_window_by_uuid
This event will be sent whenever the show desktop mode changes. E.g. when it is entered
or left.
On binding the interface the current state is sent.
This event will be sent immediately after a window is mapped.
This event will be sent when stacking order changed and on bind.
With version 17 this event is deprecated and will no longer be sent.
This event will be sent when stacking order changed and on bind.
With version 17 this event is deprecated and will no longer be sent.
This event will be sent immediately after a window is mapped.
This event will be sent when stacking order changed.
Manages and control an application window.
Only one client can bind this interface at a time.
Set window state.
Values for state argument are described by org_kde_plasma_window_management.state
and can be used together in a bitfield. The flags bitfield describes which flags are
supposed to be set, the state bitfield the value for the set flags
Deprecated: use enter_virtual_desktop
Maps the window to a different virtual desktop.
To show the window on all virtual desktops, call the
org_kde_plasma_window.set_state request and specify a on_all_desktops
state in the bitfield.
Sets the geometry of the taskbar entry for this window.
The geometry is relative to a panel in particular.
Remove the task geometry information for a particular panel.
Close this window.
Request an interactive move for this window.
Request an interactive resize for this window.
Removes the resource bound for this org_kde_plasma_window.
The compositor will write the window icon into the provided file descriptor.
The data is a serialized QIcon with QDataStream.
This event will be sent as soon as the window title is changed.
This event will be sent as soon as the application
identifier is changed.
This event will be sent as soon as the window state changes.
Values for state argument are described by org_kde_plasma_window_management.state.
DEPRECATED: use virtual_desktop_entered and virtual_desktop_left instead
This event will be sent when a window is moved to another
virtual desktop.
It is not sent if it becomes visible on all virtual desktops though.
This event will be sent whenever the themed icon name changes. May be null.
This event will be sent immediately after the window is closed
and its surface is unmapped.
This event will be sent immediately after all initial state been sent to the client.
If the Plasma window is already unmapped, the unmapped event will be sent before the
initial_state event.
This event will be sent whenever the parent window of this org_kde_plasma_window changes.
The passed parent is another org_kde_plasma_window and this org_kde_plasma_window is a
transient window to the parent window. If the parent argument is null, this
org_kde_plasma_window does not have a parent window.
This event will be sent whenever the window geometry of this org_kde_plasma_window changes.
The coordinates are in absolute coordinates of the windowing system.
This event will be sent whenever the icon of the window changes, but there is no themed
icon name. Common examples are Xwayland windows which have a pixmap based icon.
The client can request the icon using get_icon.
This event will be sent when the compositor has set the process id this window belongs to.
This should be set once before the initial_state is sent.
Make the window enter a virtual desktop. A window can enter more
than one virtual desktop. if the id is empty or invalid, no action will be performed.
RFC: do this with an empty id to request_enter_virtual_desktop?
Make the window enter a new virtual desktop. If the server consents the request,
it will create a new virtual desktop and assign the window to it.
Make the window exit a virtual desktop. If it exits all desktops it will be considered on all of them.
This event will be sent when the window has entered a new virtual desktop. The window can be on more than one desktop, or none: then is considered on all of them.
This event will be sent when the window left a virtual desktop. If the window leaves all desktops, it can be considered on all.
If the window gets manually added on all desktops, the server has to send virtual_desktop_left for every previous desktop it was in for the window to be really considered on all desktops.
This event will be sent after the application menu
for the window has changed.
Make the window enter an activity. A window can enter more activity. If the id is empty or invalid, no action will be performed.
Make the window exit a an activity. If it exits all activities it will be considered on all of them.
This event will be sent when the window has entered an activity. The window can be on more than one activity, or none: then is considered on all of them.
This event will be sent when the window left an activity. If the window leaves all activities, it will be considered on all.
If the window gets manually added on all activities, the server has to send activity_left for every previous activity it was in for the window to be really considered on all activities.
Requests this window to be displayed in a specific output.
This event will be sent when the X11 resource name of the window has changed.
This is only set for XWayland windows.
The activation manager interface provides a way to get notified
when an application is about to be activated.
Destroy the activation manager object. The activation objects introduced
by this manager object will be unaffected.
Will be issued when an app is set to be activated. It offers
an instance of org_kde_plasma_activation that will tell us the app_id
and the extent of the activation.
Notify the compositor that the org_kde_plasma_activation object will no
longer be used.
When this object is created, the compositor sends a window event for
each window in the stacking order, and afterwards sends the done event
and destroys this object.
kylin-wayland-compositor/protocols/plasma-shell.xml 0000664 0001750 0001750 00000027473 15160460057 021630 0 ustar feng feng
This interface is used by KF5 powered Wayland shells to communicate with
the compositor and can only be bound one time.
Create a shell surface for an existing surface.
Only one shell surface can be associated with a given
surface.
An interface that may be implemented by a wl_surface, for
implementations that provide the shell user interface.
It provides requests to set surface roles, assign an output
or set the position in output coordinates.
On the server side the object is automatically destroyed when
the related wl_surface is destroyed. On client side,
org_kde_plasma_surface.destroy() must be called before
destroying the wl_surface object.
The org_kde_plasma_surface interface is removed from the
wl_surface object that was turned into a shell surface with the
org_kde_plasma_shell.get_surface request.
The shell surface role is lost and wl_surface is unmapped.
Assign an output to this shell surface.
The compositor will use this information to set the position
when org_kde_plasma_surface.set_position request is
called.
Move the surface to new coordinates.
Coordinates are global, for example 50,50 for a 1920,0+1920x1080 output
is 1970,50 in global coordinates space.
Use org_kde_plasma_surface.set_output to assign an output
to this surface.
Assign a role to a shell surface.
The compositor handles surfaces depending on their role.
See the explanation below.
This request fails if the surface already has a role, this means
the surface role may be assigned only once.
== Surfaces with splash role ==
Splash surfaces are placed above every other surface during the
shell startup phase.
The surfaces are placed according to the output coordinates.
No size is imposed to those surfaces, the shell has to resize
them according to output size.
These surfaces are meant to hide the desktop during the startup
phase so that the user will always see a ready to work desktop.
A shell might not create splash surfaces if the compositor reveals
the desktop in an alternative fashion, for example with a fade
in effect.
That depends on how much time the desktop usually need to prepare
the workspace or specific design decisions.
This specification doesn't impose any particular design.
When the startup phase is finished, the shell will send the
org_kde_plasma.desktop_ready request to the compositor.
== Surfaces with desktop role ==
Desktop surfaces are placed below all other surfaces and are used
to show the actual desktop view with icons, search results or
controls the user will interact with. What to show depends on the
shell implementation.
The surfaces are placed according to the output coordinates.
No size is imposed to those surfaces, the shell has to resize
them according to output size.
Only one surface per output can have the desktop role.
== Surfaces with dashboard role ==
Dashboard surfaces are placed above desktop surfaces and are used to
show additional widgets and controls.
The surfaces are placed according to the output coordinates.
No size is imposed to those surfaces, the shell has to resize
them according to output size.
Only one surface per output can have the dashboard role.
== Surfaces with config role ==
A configuration surface is shown when the user wants to configure
panel or desktop views.
Only one surface per output can have the config role.
TODO: This should grab the input like popup menus, right?
== Surfaces with overlay role ==
Overlays are special surfaces that shows for a limited amount
of time. Such surfaces are useful to display things like volume,
brightness and status changes.
Compositors may decide to show those surfaces in a layer above
all surfaces, even full screen ones if so is desired.
== Surfaces with notification role ==
Notification surfaces display informative content for a limited
amount of time. The compositor may decide to show them in a corner
depending on the configuration.
These surfaces are shown in a layer above all other surfaces except
for full screen ones.
== Surfaces with lock role ==
The lock surface is shown by the compositor when the session is
locked, users interact with it to unlock the session.
Compositors should move lock surfaces to 0,0 in output
coordinates space and hide all other surfaces for security sake.
For the same reason it is recommended that clients make the
lock surface as big as the screen.
Only one surface per output can have the lock role.
The panel is on top of other surfaces, windows cannot cover (full screen
windows excluded).
The panel is hidden automatically and restored when the mouse is over.
Windows can cover the panel.
Maximized windows take the whole screen space but the panel is above
the windows.
Set flags bitmask as described by the flag enum.
Pass 0 to unset any flag, the surface will adjust its behavior to
the default.
Deprecated in Plasma 6. Setting this flag will have no effect. Applications should use layer shell where appropriate.
Setting this bit to the window, will make it say it prefers to not be listed in the taskbar. Taskbar implementations may or may not follow this hint.
A panel surface with panel_behavior auto_hide can perform this request to hide the panel
on a screen edge without unmapping it. The compositor informs the client about the panel
being hidden with the event auto_hidden_panel_hidden.
The compositor will restore the visibility state of the
surface when the pointer touches the screen edge the panel borders. Once the compositor restores
the visibility the event auto_hidden_panel_shown will be sent. This event will also be sent
if the compositor is unable to hide the panel.
The client can also request to show the panel again with the request panel_auto_hide_show.
A panel surface with panel_behavior auto_hide can perform this request to show the panel
again which got hidden with panel_auto_hide_hide.
By default various org_kde_plasma_surface roles do not take focus and cannot be
activated. With this request the compositor can be instructed to pass focus also to this
org_kde_plasma_surface.
An auto-hiding panel got hidden by the compositor.
An auto-hiding panel got shown by the compositor.
Setting this bit will indicate that the window prefers not to be listed in a switcher.
Request the initial position of this surface to be under the current
cursor position. Has to be called before attaching any buffer to this surface.
kylin-wayland-compositor/protocols/wlr-virtual-pointer-unstable-v1.xml 0000664 0001750 0001750 00000015357 15160460057 025351 0 ustar feng feng
Copyright © 2019 Josef Gajdusek
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
This protocol allows clients to emulate a physical pointer device. The
requests are mostly mirror opposites of those specified in wl_pointer.
The pointer has moved by a relative amount to the previous request.
Values are in the global compositor space.
The pointer has moved in an absolute coordinate frame.
Value of x can range from 0 to x_extent, value of y can range from 0
to y_extent.
A button was pressed or released.
Scroll and other axis requests.
Indicates the set of events that logically belong together.
Source information for scroll and other axis.
Stop notification for scroll and other axes.
Discrete step information for scroll and other axes.
This event allows the client to extend data normally sent using the axis
event with discrete value.
This object allows clients to create individual virtual pointer objects.
Creates a new virtual pointer. The optional seat is a suggestion to the
compositor.
Creates a new virtual pointer. The seat and the output arguments are
optional. If the seat argument is set, the compositor should assign the
input device to the requested seat. If the output argument is set, the
compositor should map the input device to the requested output.
kylin-wayland-compositor/protocols/ukui-blur-v1.xml 0000664 0001750 0001750 00000010073 15160461067 021477 0 ustar feng feng
This protocol provides a way to improve visuals of translucent surfaces
by blurring background behind them.
Warning! The protocol described in this file is currently in the testing
phase. Backward compatible changes may be added together with the
corresponding interface version bump. Backward incompatible changes can
only be done by creating a new major version of the extension.
Informs the server that the client will no longer be using this
protocol object. Existing objects created by this object are not
affected.
Instantiate an interface extension for the given wl_surface to
blur background behind it. If the given wl_surface already has
a ukui_blur_surface_v1 object associated, the blur_exists protocol
error will be raised.
The blur object provides a way to specify a region behind a surface
that should be blurred by the compositor.
If the wl_surface associated with the ukui_blur_surface_v1 object has been
destroyed, this object becomes inert.
Informs the server that the client will no longer be using this
protocol object. The blur region will be unset on the next commit.
This request sets the region of the surface that will have its
background blurred.
The blur region is specified in the surface-local coordinates.
The initial value for the blur region is empty. Setting the pending
blur region has copy semantics, and the wl_region object can be
destroyed immediately. A NULL wl_region causes the blur region to be
set to infinite.
The blur region is double-buffered state, and will be applied on
the next wl_surface.commit.
The blur algorithm is subject to compositor policies.
Change blur algorithm level in compositor.
The range of level is 1 to 15.
The compositor default blur algorithm level is 5.
The blur level will be applied on the next wl_surface.commit.
The opacity of blur, value between 0 and 1.0.
The mixture of original background and blur. Default value is 1.0.
The blur opacity will be applied on the next wl_surface.commit.
kylin-wayland-compositor/protocols/ukui-effect-v1.xml 0000664 0001750 0001750 00000021572 15160460057 021773 0 ustar feng feng
This interface adds or modifies the surface effect.
It provides requests to create effect animation and effect for the surface.
Only one effect can be associated for the surface on the same stage.
Create a new private effect animation use bezier curves.
X1 and y1 correspond to the first control point (P1), x2 and y2 correspond to
the second control point (P2). Need to divide the value by 100.
The new created animation can only be used by itself.
Create a fade effect for an existing surface.
Values for stage argument are described by surface_effect_v1.stage
and can be used together in a bitfield, these stages will use the same parameters.
Create a slide effect for an existing surface.
Values for stage argument are described by surface_effect_v1.stage
and can be used together in a bitfield, these stages will use the same parameters.
Emitted after bind surface_effect_v1.
Every builtin animation will send an event.
This event is sent after all builtin animation curves have been sent on binding to
the surface_effect_v1 object.
This request indicates that the client will no longer use this animation object.
An 'set_animation' request must be called once per fade effect.
It will use default curve when curve set to be null.
Duration is the time between effect start and end.
The default value of fade effect has two different values. Duration is 220ms when used at
view map stage, and is 180ms when used at view unmap stage.
An 'set_scale' request must be called once per scale effect.
If value is 80, means scaled to 80% of the original width or height.
Value range is 0-100. If value is 80, means the alpha value of surface color is 0.8.
It will use default value when not call this request to set opacity.
This request indicates that the client will no longer use this effect object.
Duration is the time between slide effect start and end.
An 'set_position' request must be called once per slide effect.
Value range is 0-100. If value is 80, means the alpha value of surface color is 0.8.
It will use default value when not call this request to set opacity.
The default value is slide_on_node.
location is edge of the node, offset is relative to edge of the node when type is slide_on_node.
location is edge of the screen, offset is relative to edge of the screen when type is slide_on_output.
This request indicates that the client will no longer use this effect object.
kylin-wayland-compositor/protocols/ukui-window-management.xml 0000664 0001750 0001750 00000041702 15160460057 023631 0 ustar feng feng
This interface manages application windows. It provides requests to show and hide the desktop
and emits an event every time a window is created so that the client can use it to manage the
window.
Tell the compositor to show/hide the desktop.
This event will be sent whenever the show desktop mode changes. E.g. when it is entered or
left.
On binding the interface the current state is sent.
This event will be sent when stacking order changed and on bind
This event will be sent immediately after a window is mapped.
Manages and control an application window.
Set window state. Can set multiple states at once.
Values for state argument are described by ukui_window_management.state and can be used
together in a bitfield.
The flags bitfield describes which flags are supposed to be set, the
state bitfield the value for the set flags.
The state argument is not a boolean value, but a bitfield, so it is possible to set multiple
states at once. Only the states that are set in the flags bitfield will be changed.
For example:
If flags was 0x14(keep_above|maximized) and state was 0x04(maximized), the window would be
maximized, but not keep above.
If flags was 0x04(maximized) and state was 0x14(keep_above|maximized), the window would be
maximized but not change keep above state.
Sets the geometry of the taskbar/desktop entry for this window. The geometry is relative to
a panel/desktop in particular.
Sets the geometry of the taskbar entry for this window. The geometry is relative to a panel
in particular.
Remove the task geometry information for a particular panel.
Close this window.
Request an interactive move for this window. The pointer will move to the center of the
window and move with the pointer.
When mouse button is released, the interactive move ends.
Request an interactive resize for this window. The pointer will move to window right bottom
corner and resize with the pointer.
When mouse button is released, the interactive resize ends.
Removes the resource bound for this ukui_window.
The compositor will write the window icon into the provided file descriptor. The data is a
serialized QIcon with QDataStream.
This event will be sent as soon as the window title is changed.
This event will be sent as soon as the application identifier is changed.
This event will be sent as soon as the window state changes.
Values for state argument are described by ukui_window_management.state. It contains
the whole new state of the window.
This event will be sent whenever the themed icon name changes. May be null.
This event will be sent immediately after the window is closed and its surface is unmapped.
This event will be sent immediately after all initial state been sent to the client. If the
Plasma window is already unmapped, the unmapped event will be sent before the initial_state
event.
This event will be sent whenever the parent window of this ukui_window changes. The
passed parent is another ukui_window and this ukui_window is a transient window to
the parent window. If the parent argument is null, this ukui_window does not have a
parent window.
This event will be sent whenever the window geometry of this ukui_window changes. The
coordinates are in absolute coordinates of the windowing system.
This event will be sent whenever the icon of the window changes, but there is no themed icon
name. Common examples are Xwayland windows which have a pixmap based icon.
The client can request the icon using get_icon.
This event will be sent when the compositor has set the process id this window belongs to.
This should be set once before the initial_state is sent.
Make the window enter a virtual desktop. A window can enter more than one virtual desktop.
if the id is empty or invalid, no action will be performed.
RFC: do this with an empty id to
request_enter_virtual_desktop?
Make the window enter a new virtual desktop. If the server consents the request, it will
create a new virtual desktop and assign the window to it.
Make the window exit a virtual desktop. If it exits all desktops it will be considered on
all of them.
This event will be sent when the window has entered a new virtual desktop. The window can be
on more than one desktop, or none: then is considered on all of them.
This event will be sent when the window left a virtual desktop. If the window leaves all
desktops, it can be considered on all. If the window gets manually added on all desktops,
the server has to send virtual_desktop_left for every previous desktop it was in for the
window to be really considered on all desktops.
This event will be sent after the application menu for the window has changed.
Make the window enter an activity. A window can enter more activity. If the id is empty or
invalid, no action will be performed.
Make the window exit a an activity. If it exits all activities it will be considered on all
of them.
This event will be sent when the window has entered an activity. The window can be on more
than one activity, or none: then is considered on all of them.
This event will be sent when the window left an activity. If the window leaves all
activities, it will be considered on all. If the window gets manually added on all
activities, the server has to send activity_left for every previous activity it was in for
the window to be really considered on all activities.
Requests this window to be displayed in a specific output.
This event will be sent when the X11 resource name of the window has changed. This is only
set for XWayland windows.
Tell the compositor to highlight this window.
Tell the compositor to unset highlight window.
The activation manager interface provides a way to get notified when an application is about
to be activated.
Destroy the activation manager object. The activation objects introduced by this manager
object will be unaffected.
Will be issued when an app is set to be activated. It offers an instance of
org_ukui_activation that will tell us the app_id and the extent of the activation.
Notify the compositor that the org_ukui_activation object will no longer be used.
kylin-wayland-compositor/protocols/drm.xml 0000664 0001750 0001750 00000017447 15160460057 020030 0 ustar feng feng
Copyright © 2008-2011 Kristian Høgsberg
Copyright © 2010-2011 Intel Corporation
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that\n the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, 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.
Bitmask of capabilities.
kylin-wayland-compositor/protocols/wlr-output-power-management-unstable-v1.xml 0000664 0001750 0001750 00000012733 15160460057 027004 0 ustar feng feng
Copyright © 2019 Purism SPC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
This protocol allows clients to control power management modes
of outputs that are currently part of the compositor space. The
intent is to allow special clients like desktop shells to power
down outputs when the system is idle.
To modify outputs not currently part of the compositor space see
wlr-output-management.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
This interface is a manager that allows creating per-output power
management mode controls.
Create a output power management mode control that can be used to
adjust the power management mode for a given output.
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
This object offers requests to set the power management mode of
an output.
Set an output's power save mode to the given mode. The mode change
is effective immediately. If the output does not support the given
mode a failed event is sent.
Report the power management mode change of an output.
The mode event is sent after an output changed its power
management mode. The reason can be a client using set_mode or the
compositor deciding to change an output's mode.
This event is also sent immediately when the object is created
so the client is informed about the current power management mode.
This event indicates that the output power management mode control
is no longer valid. This can happen for a number of reasons,
including:
- The output doesn't support power management
- Another client already has exclusive power management mode control
for this output
- The output disappeared
Upon receiving this event, the client should destroy this object.
Destroys the output power management mode control object.
kylin-wayland-compositor/protocols/plasma-virtual-desktop.xml 0000664 0001750 0001750 00000012543 15160460057 023646 0 ustar feng feng
Given the id of a particular virtual desktop, get the corresponding org_kde_plasma_virtual_desktop which represents only the desktop with that id.
Ask the server to create a new virtual desktop, and position it at a specified position. If the position is zero or less, it will be positioned at the beginning, if the position is the count or more, it will be positioned at the end.
Ask the server to get rid of a virtual desktop, the server may or may not acconsent to the request.
This event is sent after all other properties has been
sent after binding to the desktop manager object and after any
other property changes done after that. This allows
changes to the org_kde_plasma_virtual_desktop_management properties to be seen as
atomic, even if they happen via multiple events.
Request the server to set the status of this desktop to active: The server is free to consent or deny the request. This will be the new "current" virtual desktop of the system.
The format of the id is decided by the compositor implementation. A desktop id univocally identifies a virtual desktop and must be guaranteed to never exist two desktops with the same id. The format of the string id is up to the server implementation.
The desktop will be the new "current" desktop of the system. The server may support either one virtual desktop active at a time, or other combinations such as one virtual desktop active per screen.
Windows associated to this virtual desktop will be shown.
Windows that were associated only to this desktop will be hidden.
This event is sent after all other properties has been
sent after binding to the desktop object and after any
other property changes done after that. This allows
changes to the org_kde_plasma_virtual_desktop properties to be seen as
atomic, even if they happen via multiple events.
This virtual desktop has just been removed by the server:
All windows will lose the association to this desktop.
kylin-wayland-compositor/protocols/idle.xml 0000664 0001750 0001750 00000003410 15160460057 020144 0 ustar feng feng
This interface allows to monitor user idle time on a given seat. The interface
allows to register timers which trigger after no user activity was registered
on the seat for a given interval. It notifies when user activity resumes.
This is useful for applications wanting to perform actions when the user is not
interacting with the system, e.g. chat applications setting the user as away, power
management features to dim screen, etc..
kylin-wayland-compositor/protocols/blur.xml 0000664 0001750 0001750 00000001755 15160461067 020207 0 ustar feng feng
kylin-wayland-compositor/protocols/wlr-layer-shell-unstable-v1.xml 0000664 0001750 0001750 00000044036 15160460057 024422 0 ustar feng feng
Copyright © 2017 Drew DeVault
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, 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.
Clients can use this interface to assign the surface_layer role to
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
rendered with a defined z-depth respective to each other. They may also be
anchored to the edges and corners of a screen and specify input handling
semantics. This interface should be suitable for the implementation of
many desktop shell components, and a broad number of other applications
that interact with the desktop.
Create a layer surface for an existing surface. This assigns the role of
layer_surface, or raises a protocol error if another role is already
assigned.
Creating a layer surface from a wl_surface which has a buffer attached
or committed is a client error, and any attempts by a client to attach
or manipulate a buffer prior to the first layer_surface.configure call
must also be treated as errors.
After creating a layer_surface object and setting it up, the client
must perform an initial commit without any buffer attached.
The compositor will reply with a layer_surface.configure event.
The client must acknowledge it and is then allowed to attach a buffer
to map the surface.
You may pass NULL for output to allow the compositor to decide which
output to use. Generally this will be the one that the user most
recently interacted with.
Clients can specify a namespace that defines the purpose of the layer
surface.
These values indicate which layers a surface can be rendered in. They
are ordered by z depth, bottom-most first. Traditional shell surfaces
will typically be rendered between the bottom and top layers.
Fullscreen shell surfaces are typically rendered at the top layer.
Multiple surfaces can share a single layer, and ordering within a
single layer is undefined.
This request indicates that the client will not use the layer_shell
object any more. Objects that have been created through this instance
are not affected.
An interface that may be implemented by a wl_surface, for surfaces that
are designed to be rendered as a layer of a stacked desktop-like
environment.
Layer surface state (layer, size, anchor, exclusive zone,
margin, interactivity) is double-buffered, and will be applied at the
time wl_surface.commit of the corresponding wl_surface is called.
Attaching a null buffer to a layer surface unmaps it.
Unmapping a layer_surface means that the surface cannot be shown by the
compositor until it is explicitly mapped again. The layer_surface
returns to the state it had right after layer_shell.get_layer_surface.
The client can re-map the surface by performing a commit without any
buffer attached, waiting for a configure event and handling it as usual.
Sets the size of the surface in surface-local coordinates. The
compositor will display the surface centered with respect to its
anchors.
If you pass 0 for either value, the compositor will assign it and
inform you of the assignment in the configure event. You must set your
anchor to opposite edges in the dimensions you omit; not doing so is a
protocol error. Both values are 0 by default.
Size is double-buffered, see wl_surface.commit.
Requests that the compositor anchor the surface to the specified edges
and corners. If two orthogonal edges are specified (e.g. 'top' and
'left'), then the anchor point will be the intersection of the edges
(e.g. the top left corner of the output); otherwise the anchor point
will be centered on that edge, or in the center if none is specified.
Anchor is double-buffered, see wl_surface.commit.
Requests that the compositor avoids occluding an area with other
surfaces. The compositor's use of this information is
implementation-dependent - do not assume that this region will not
actually be occluded.
A positive value is only meaningful if the surface is anchored to one
edge or an edge and both perpendicular edges. If the surface is not
anchored, anchored to only two perpendicular edges (a corner), anchored
to only two parallel edges or anchored to all edges, a positive value
will be treated the same as zero.
A positive zone is the distance from the edge in surface-local
coordinates to consider exclusive.
Surfaces that do not wish to have an exclusive zone may instead specify
how they should interact with surfaces that do. If set to zero, the
surface indicates that it would like to be moved to avoid occluding
surfaces with a positive exclusive zone. If set to -1, the surface
indicates that it would not like to be moved to accommodate for other
surfaces, and the compositor should extend it all the way to the edges
it is anchored to.
For example, a panel might set its exclusive zone to 10, so that
maximized shell surfaces are not shown on top of it. A notification
might set its exclusive zone to 0, so that it is moved to avoid
occluding the panel, but shell surfaces are shown underneath it. A
wallpaper or lock screen might set their exclusive zone to -1, so that
they stretch below or over the panel.
The default value is 0.
Exclusive zone is double-buffered, see wl_surface.commit.
Requests that the surface be placed some distance away from the anchor
point on the output, in surface-local coordinates. Setting this value
for edges you are not anchored to has no effect.
The exclusive zone includes the margin.
Margin is double-buffered, see wl_surface.commit.
Types of keyboard interaction possible for layer shell surfaces. The
rationale for this is twofold: (1) some applications are not interested
in keyboard events and not allowing them to be focused can improve the
desktop experience; (2) some applications will want to take exclusive
keyboard focus.
This value indicates that this surface is not interested in keyboard
events and the compositor should never assign it the keyboard focus.
This is the default value, set for newly created layer shell surfaces.
This is useful for e.g. desktop widgets that display information or
only have interaction with non-keyboard input devices.
Request exclusive keyboard focus if this surface is above the shell surface layer.
For the top and overlay layers, the seat will always give
exclusive keyboard focus to the top-most layer which has keyboard
interactivity set to exclusive. If this layer contains multiple
surfaces with keyboard interactivity set to exclusive, the compositor
determines the one receiving keyboard events in an implementation-
defined manner. In this case, no guarantee is made when this surface
will receive keyboard focus (if ever).
For the bottom and background layers, the compositor is allowed to use
normal focus semantics.
This setting is mainly intended for applications that need to ensure
they receive all keyboard events, such as a lock screen or a password
prompt.
This requests the compositor to allow this surface to be focused and
unfocused by the user in an implementation-defined manner. The user
should be able to unfocus this surface even regardless of the layer
it is on.
Typically, the compositor will want to use its normal mechanism to
manage keyboard focus between layer shell surfaces with this setting
and regular toplevels on the desktop layer (e.g. click to focus).
Nevertheless, it is possible for a compositor to require a special
interaction to focus or unfocus layer shell surfaces (e.g. requiring
a click even if focus follows the mouse normally, or providing a
keybinding to switch focus between layers).
This setting is mainly intended for desktop shell components (e.g.
panels) that allow keyboard interaction. Using this option can allow
implementing a desktop shell that can be fully usable without the
mouse.
Set how keyboard events are delivered to this surface. By default,
layer shell surfaces do not receive keyboard events; this request can
be used to change this.
This setting is inherited by child surfaces set by the get_popup
request.
Layer surfaces receive pointer, touch, and tablet events normally. If
you do not want to receive them, set the input region on your surface
to an empty region.
Keyboard interactivity is double-buffered, see wl_surface.commit.
This assigns an xdg_popup's parent to this layer_surface. This popup
should have been created via xdg_surface::get_popup with the parent set
to NULL, and this request must be invoked before committing the popup's
initial state.
See the documentation of xdg_popup for more details about what an
xdg_popup is and how it is used.
When a configure event is received, if a client commits the
surface in response to the configure event, then the client
must make an ack_configure request sometime before the commit
request, passing along the serial of the configure event.
If the client receives multiple configure events before it
can respond to one, it only has to ack the last configure event.
A client is not required to commit immediately after sending
an ack_configure request - it may even ack_configure several times
before its next surface commit.
A client may send multiple ack_configure requests before committing, but
only the last request sent before a commit indicates which configure
event the client really is responding to.
This request destroys the layer surface.
The configure event asks the client to resize its surface.
Clients should arrange their surface for the new states, and then send
an ack_configure request with the serial sent in this configure event at
some point before committing the new surface.
The client is free to dismiss all but the last configure event it
received.
The width and height arguments specify the size of the window in
surface-local coordinates.
The size is a hint, in the sense that the client is free to ignore it if
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
resize in steps of NxM pixels). If the client picks a smaller size and
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
surface will be centered on this axis.
If the width or height arguments are zero, it means the client should
decide its own window dimension.
The closed event is sent by the compositor when the surface will no
longer be shown. The output may have been destroyed or the user may
have asked for it to be removed. Further changes to the surface will be
ignored. The client should destroy the resource after receiving this
event, and create a new surface if they so choose.
Change the layer that the surface is rendered on.
Layer is double-buffered, see wl_surface.commit.
kylin-wayland-compositor/protocols/slide.xml 0000664 0001750 0001750 00000003123 15160460057 020330 0 ustar feng feng
Ask the compositor to move the surface from a location to another
with a slide animation.
The from argument provides a clue about where the slide animation
begins, offset is the distance from screen edge to begin the animation.
kylin-wayland-compositor/protocols/wlr-screencopy-unstable-v1.xml 0000664 0001750 0001750 00000023666 15160460057 024361 0 ustar feng feng
Copyright © 2018 Simon Ser
Copyright © 2019 Andri Yngvason
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
This protocol allows clients to ask the compositor to copy part of the
screen content to a client buffer.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
This object is a manager which offers requests to start capturing from a
source.
Capture the next frame of an entire output.
Capture the next frame of an output's region.
The region is given in output logical coordinates, see
xdg_output.logical_size. The region will be clipped to the output's
extents.
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
This object represents a single frame.
When created, a series of buffer events will be sent, each representing a
supported buffer type. The "buffer_done" event is sent afterwards to
indicate that all supported buffer types have been enumerated. The client
will then be able to send a "copy" request. If the capture is successful,
the compositor will send a "flags" followed by a "ready" event.
For objects version 2 or lower, wl_shm buffers are always supported, ie.
the "buffer" event is guaranteed to be sent.
If the capture failed, the "failed" event is sent. This can happen anytime
before the "ready" event.
Once either a "ready" or a "failed" event is received, the client should
destroy the frame.
Provides information about wl_shm buffer parameters that need to be
used for this frame. This event is sent once after the frame is created
if wl_shm buffers are supported.
Copy the frame to the supplied buffer. The buffer must have a the
correct size, see zwlr_screencopy_frame_v1.buffer and
zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a
supported format.
If the frame is successfully copied, a "flags" and a "ready" events are
sent. Otherwise, a "failed" event is sent.
Provides flags about the frame. This event is sent once before the
"ready" event.
Called as soon as the frame is copied, indicating it is available
for reading. This event includes the time at which presentation happened
at.
The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples,
each component being an unsigned 32-bit value. Whole seconds are in
tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo,
and the additional fractional part in tv_nsec as nanoseconds. Hence,
for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part
may have an arbitrary offset at start.
After receiving this event, the client should destroy the object.
This event indicates that the attempted frame copy has failed.
After receiving this event, the client should destroy the object.
Destroys the frame. This request can be sent at any time by the client.
Same as copy, except it waits until there is damage to copy.
This event is sent right before the ready event when copy_with_damage is
requested. It may be generated multiple times for each copy_with_damage
request.
The arguments describe a box around an area that has changed since the
last copy request that was derived from the current screencopy manager
instance.
The union of all regions received between the call to copy_with_damage
and a ready event is the total damage since the prior ready event.
Provides information about linux-dmabuf buffer parameters that need to
be used for this frame. This event is sent once after the frame is
created if linux-dmabuf buffers are supported.
This event is sent once after all buffer events have been sent.
The client should proceed to create a buffer of one of the supported
types, and send a "copy" request.
kylin-wayland-compositor/protocols/kywc-capture-v1.xml 0000664 0001750 0001750 00000016701 15160460057 022200 0 ustar feng feng
This object is a manager which offers requests to start capturing from a source.
Capture the next frame of an entire output.
Capture the next frame of an entire workspace in one output.
Capture the next frame of an entire toplevel.
Capture the cursor of the seat and frame.
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
Destroys the frame. This request can be sent at any time by the client.
This event indicates that the attempted frame copy has failed.
After receiving this event, the client should destroy the object.
Sent if the compositor cancels the frame because the source has gone.
Upon receiving this event, the client should destroy this object.
Provides information about buffer parameters that need to be
used for this frame.
Mark the buffer is not used by client. This request is sent after buffer event.
Provides information about buffer parameters with the plane index
that need to be used for this frame.
When the seat or frame is destroyed, this object becomes inert.
Destroys the object. This request can be sent at any time by the client.
This event indicates that when a cursor enters the frame. It shall be
generated before the "position" and "hotspot" events.
If the pointer object referenced by the seat does not exist, this event
will not be triggered.
This event indicates that when a cursor leaves the frame. No "position" or
"hotspot" event is generated for the cursor until the cursor enters the
frame again.
If the pointer object referenced by the seat is removed, this event will be sent.
This event informs the cursor position. The given position is
relative to the frame's top left corner in transformed buffer
pixel coordinates.
Note, if the frame parameter is set to null in the capture_cursor request,
the given position coordinates are global.
This event informs the cursor hotspot. The hotspot describes the offset
between the cursor image and the position of the input device.
The parameters x and y represent the offset of the new hotspot relative
to the origin of the cursor image, which is typically the top-left corner
of the image.
kylin-wayland-compositor/protocols/kde-primary-output-v1.xml 0000664 0001750 0001750 00000001716 15160460057 023344 0 ustar feng feng
SPDX-License-Identifier: Expat-CMU
]]>
Protocol for telling which is the primary display among the selection of enabled outputs.
Specifies which output is the primary one identified by their uuid. See kde_output_device_v2 uuid event for more information about it.
kylin-wayland-compositor/protocols/kywc-toplevel-v1.xml 0000664 0001750 0001750 00000030360 15160460057 022364 0 ustar feng feng
A toplevel is defined as a surface with a role similar to xdg_toplevel.
XWayland surfaces may be treated like toplevels in this protocol.
After a client binds the global, each mapped toplevel window will be sent
using the toplevel event.
This event is emitted whenever a new toplevel window is created. It is
emitted for all toplevels, regardless of the app that has created them.
All initial properties of the toplevel (identifier, title, app_id) will be sent
immediately after this event using the corresponding events for kywc_toplevel.
The compositor will use the done event to indicate when all data has been sent.
This event indicates that the compositor is done sending events
to this object. The client should destroy the object.
See destroy for more information.
The compositor must not send any more toplevel events after this event.
This request indicates that the client no longer wishes to receive
events for new toplevels.
The Wayland protocol is asynchronous, meaning the compositor may send
further toplevel events until the stop request is processed.
The client should wait for a finished event before destroying this object.
A kywc_toplevel_v1 object represents a mapped toplevel window.
A single app may have multiple mapped toplevels.
This request should be used when the client will no longer use the toplevel
or after the closed event has been received to allow destruction of the object.
The server will emit no further events on the kywc_toplevel_v1
after this event. Any requests received aside from the destroy request must
be ignored. Upon receiving this event, the client should destroy the handle.
This event is sent after all changes in the toplevel state have been sent.
The title of the toplevel has changed.
The app id of the topleve.done for details.
This event is emitted whenever the output that the largest area of this toplevel
is displayed on has changed.
This event is emitted whenever the toplevel becomes visible on
the given workspace. A toplevel may be visible on multiple workspaces.
This event is emitted whenever the toplevel stops being visible on
the given workspace. It is guaranteed that an entered-workspace event
with the same workspace has been emitted before this event.
Describes what capabilities this toplevel has.
What capabilities this toplevel has, sent on startup before the first done event.
The different states that a toplevel can have.
This event is emitted immediately after the kywc_toplevel_v1
is created and each time the toplevel state changes, either because of a
compositor action or because of a request in this protocol.
This event is emitted whenever the parent of the toplevel changes.
No event is emitted when the parent handle is destroyed by the client.
This event will be sent whenever the icon changes. May be null.
This event will be sent whenever the toplevel geometry changes.
Requests that the toplevel be maximized. If the maximized state actually
changes, this will be indicated by the state event.
Requests that the toplevel be unmaximized. If the maximized state actually
changes, this will be indicated by the state event.
Requests that the toplevel be minimized. If the minimized state actually
changes, this will be indicated by the state event.
Requests that the toplevel be unminimized. If the minimized state actually
changes, this will be indicated by the state event.
Requests that the toplevel be fullscreened on the given output. If the
fullscreen state and/or the outputs the toplevel is visible on actually
change, this will be indicated by the state and output_enter/leave
events.
The output parameter is only a hint to the compositor. Also, if output
is NULL, the compositor should decide which output the toplevel will be
fullscreened on, if at all.
Requests that the toplevel be unfullscreened. If the fullscreen state
actually changes, this will be indicated by the state event.
Request that this toplevel be activated.
There is no guarantee the toplevel will be actually activated.
Send a request to the toplevel to close itself. The compositor would
typically use a shell-specific method to carry out this request, for
example by sending the xdg_toplevel.close event. However, this gives
no guarantees the toplevel will actually be destroyed. If and when
this happens, the closed event will be emitted.
Make the toplevel enter a workspace. A toplevel can enter more
than one workspace. if the id is empty or invalid, no action will be performed.
Make the toplevel exit a workspace.
Requests this toplevel to be displayed only in a specific workspace.
Requests this toplevel to be displayed in a specific output.
Move the window to new coordinates.
Set the window to new size.
Window size will be consider compositor window size limits.
This event will only be emitted once, before the first "done" event occurs.
kylin-wayland-compositor/protocols/dpms.xml 0000664 0001750 0001750 00000010165 15160460057 020177 0 ustar feng feng
The Dpms manager allows to get a org_kde_kwin_dpms for a given wl_output.
The org_kde_kwin_dpms provides the currently used VESA Display Power Management
Signaling state (see https://en.wikipedia.org/wiki/VESA_Display_Power_Management_Signaling ).
In addition it allows to request a state change. A compositor is not obliged to honor it
and will normally automatically switch back to on state.
Factory request to get the org_kde_kwin_dpms for a given wl_output.
This interface provides information about the VESA DPMS state for a wl_output.
It gets created through the request get on the org_kde_kwin_dpms_manager interface.
On creating the resource the server will push whether DPSM is supported for the output,
the currently used DPMS state and notifies the client through the done event once all
states are pushed. Whenever a state changes the set of changes is committed with the
done event.
This event gets pushed on binding the resource and indicates whether the wl_output
supports DPMS. There are operation modes of a Wayland server where DPMS might not
make sense (e.g. nested compositors).
This mode gets pushed on binding the resource and provides the currently used
DPMS mode. It also gets pushed if DPMS is not supported for the wl_output, in that
case the value will be On.
The event is also pushed whenever the state changes.
This event gets pushed on binding the resource once all other states are pushed.
In addition it gets pushed whenever a state changes to tell the client that all
state changes have been pushed.
Requests that the compositor puts the wl_output into the passed mode. The compositor
is not obliged to change the state. In addition the compositor might leave the mode
whenever it seems suitable. E.g. the compositor might return to On state on user input.
The client should not assume that the mode changed after requesting a new mode.
Instead the client should listen for the mode event.
kylin-wayland-compositor/protocols/kywc-workspace-v1.xml 0000664 0001750 0001750 00000015271 15160460057 022534 0 ustar feng feng
Workspaces, also called virtual desktops, are groups of surfaces. A
compositor with a concept of workspaces may only show some such groups of
surfaces (those of 'active' workspaces) at a time. 'Activating' a
workspace is a request for the compositor to display that workspace's
surfaces as normal, whereas the compositor may hide or otherwise
de-emphasise surfaces that are associated only with 'inactive' workspaces.
After a client binds the kywc_workspace_manager_v1, each workspace will be
sent via the workspace event.
This event is emitted whenever a new workspace has been created.
All initial details of the workspace (name, position, activated) will
be sent immediately after this event via the corresponding events in
kywc_workspace_v1.
Request that the compositor create a new workspace with the given name
and position it at a specified position.
If the position is zero or less, it will be positioned at the beginning,
if the position is the count or more, it will be positioned at the end.
There is no guarantee that the compositor will create a new workspace,
or that the created workspace will have the provided name and position.
This event is sent after all changes in all workspaces have been sent.
This allows changes to one or more kywc_workspace_v1 properties
to be seen as atomic, even if they happen via multiple events.
This event indicates that the compositor is done sending events to the
kywc_workspace_manager_v1. The server will destroy the object
immediately after sending this request, so it will become invalid and
the client should free any resources associated with it.
Indicates the client no longer wishes to receive events for new workspace.
However the compositor may emit further workspace events,
until the finished event is emitted. The compositor is expected
to send the finished event eventually once the stop request has been processed.
The client must not send any more requests after this one.
A kywc_workspace_v1 object represents a workspace that handles a
group of surfaces.
Each workspace has a name, conveyed to the client with the name event;
and a position, conveyed to the client with the position event.
The client may request that the compositor activate or deactivate the workspace.
This event is emitted immediately after the kywc_workspace_v1 is
created and whenever the name of the workspace changes.
The workspace will be the new "current" workspace of the system.
surfaces that were associated only to this workspace will be hidden.
This event means the kywc_workspace_v1 has been destroyed. It is
guaranteed there won't be any more events for this
kywc_workspace_v1. The kywc_workspace_v1 becomes inert so
any requests will be ignored except the destroy request.
Destroys the kywc_workspace_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
If the position is zero or less, it will be positioned at the beginning,
if the position is the count or more, it will be positioned at the end.
Request that this workspace be activated.
There is no guarantee the workspace will be actually activated, and
behaviour may be compositor-dependentption
Request that this workspace be removed.
There is no guarantee the workspace will be actually removed.
Set the workspace to new name.
kylin-wayland-compositor/protocols/ukui-startup-v2.xml 0000664 0001750 0001750 00000004436 15160460057 022242 0 ustar feng feng
This interface manages startup infos.
The startup_info object provides a way sets the startup geometry of the
taskbar/desktop entry for app.
The object will auto destroy when arrive the retire time.
The retire time is 5000ms.
Sets the startup geometry of the taskbar/desktop entry.
Coordinates are global.
The pid determines which process will use the startup_geometry.
The child process of this process will also use the startup_geometry.
The appid determines which app should use the startup_geometry.
It only uses when not set pid or not find the app from pid.
Must call destroy after set info completely.
kylin-wayland-compositor/protocols/kde-keystate.xml 0000664 0001750 0001750 00000002617 15160460057 021631 0 ustar feng feng
SPDX-License-Identifier: LGPL-2.1-or-later
]]>
Keeps track of the states of the different keys that have a state attached to it.
kylin-wayland-compositor/protocols/ukui-shell-v1.xml 0000664 0001750 0001750 00000034771 15160461067 021655 0 ustar feng feng
This interface is used by UKUI Wayland shells to communicate with the compositor and
can only be bound one time.
Create a shell surface for an existing surface.
Only one shell surface can be associated with a given surface.
Should create ukui_surface_v1 before create xdg_surface and surface map.
Request get the output under cursor per seat.
Send current_output and done event after this request.
Create a shell decoration for an existing surface.
Only one shell decoration can be associated with a given surface.
This request needs be called before surface map.
Emitted after bind ukui_shell or receive get_current_output request.
This event is sent after all information have been sent on binding to
the ukui shell object.
An interface that may be implemented by a wl_surface, for implementations that provide the
shell user interface.
It provides requests to set surface roles, state or set the position in global
coordinates.
On the server side the object is automatically destroyed when the related wl_surface is
destroyed. On client side, ukui_surface_v1.destroy() must be called before destroying the
wl_surface object.
The ukui_surface_v1 interface is removed from the wl_surface object that was turned into a
shell surface with the ukui_shell_v1.get_surface request.
Move the surface to new coordinates. Coordinates are global.
Setting this will say the surface prefers to not be listed in the taskbar.
This request needs be called before surface map.
Setting this will say the surface prefers to not be listed in the switcher.
This request needs be called before surface map.
Assign a role to a shell surface.
The compositor handles surfaces depending on their role.
This request needs be called before surface map.
Set surface state. Can set multiple states at once.
Values for state argument are described by ukui_surface.state and can be used
together in a bitfield.
The flags bitfield describes which flags are supposed to be set, the
state bitfield the value for the set flags.
The state argument is not a boolean value, but a bitfield, so it is possible to set multiple
states at once. Only the states that are set in the flags bitfield will be changed.
The keep_above and keep_below can only used for the normal window.
For example:
If flags was 0x12(movable|maximizable) and state was 0x02(maximizable), the window would be
maximizable, but not movable.
Setting this bit will indicate that the panel area not to be calculate in output usable area.
Request the initial position of this surface to be under the current cursor position.
This request needs be called before surface map.
This request makes the created surface take an explicit keyboard grab.
This keyboard grab will be dismissed when the client destroys the surface.
Grab all keyboard when seat is set to null.
Setting server decoration icon by icon name.
Set icon name to null will use app_id to find icon.
This request needs be called before surface map.
This event informs the client that the position of the surface is about to change.
This request makes the created surface to be activated.
There is no guarantee the surface will be actually activated, and behaviour
may be compositor-dependentption.
Sets the startup geometry of the taskbar/desktop entry for this surface.
Coordinates are global.
One established way of doing this is through the UKUI_SURFACE_STARTUP_GEOMETRY
environment variable of a newly launched child process. The child process
should unset the environment variable again right after reading it out in
order to avoid propagating it to other child processes.
This request needs be called before surface map.
This request is for showing tile flyout on the client side decoration.
The parameters represent a mouse region, tile flyout will autohide when mouse leave
the region.
Coordinates are relative to the surface.
Use default seat when seat is set to null.
This request needs be called after surface map.
This event will be sent as soon as the surface state changes.
Values for state argument are described by ukui_surface_v1.surface_state. It contains
the whole new state of the window.
Set the restore geometry for this surface.
The restore geometry is used for window doing restore from maximized/fullscreen state.
Coordinates are global.
Move the surface to new coordinates. Coordinates are global.
The position will be applied on the next wl_surface.commit.
It provides requests to set surface need to draw corner/shadow/border or not.
On the server side the object is automatically destroyed when the related wl_surface is
destroyed. On client side, ukui_decoration_v1.destroy() must be called before destroying the
wl_surface object.
Setting this will say the surface prefers not to show titlebar.
This request needs be called before surface map.
The flags bitfield describes which components of decoration are supposed to be set and can be set
together in a bitfield. Draw decoration will be based on the flags.
This flags needs be called before surface map and become immutable when surface is mapped.
This request is only for xdg_popup now.
kylin-wayland-compositor/src/ 0000775 0001750 0001750 00000000000 15160461067 015254 5 ustar feng feng kylin-wayland-compositor/src/config/ 0000775 0001750 0001750 00000000000 15160461067 016521 5 ustar feng feng kylin-wayland-compositor/src/config/kde_input.c 0000664 0001750 0001750 00000076223 15160461067 020661 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include "config_p.h"
#include "input/input.h"
#include "kywc/output.h"
#include "server.h"
#include "util/dbus.h"
static const char *service_path = "/org/kde/KWin/InputDevice";
static const char *kde_input_path = "/org/kde/KWin/InputDevice/";
static const char *service_interface = "org.kde.KWin.InputDeviceManager";
static const char *kde_input_interface = "org.kde.KWin.InputDevice";
#define KDE_PROP(name, type, read) \
SD_BUS_PROPERTY(name, type, read, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)
#define KDE_WPROP(name, type, read, write) \
SD_BUS_WRITABLE_PROPERTY(name, type, read, write, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)
struct kde_input {
struct wl_list link;
struct dbus_object *dbus;
char *sys_name;
struct input *input;
struct wl_listener destroy;
};
struct kde_input_manager {
struct wl_list inputs;
struct config *config;
struct wl_listener new_input;
struct wl_listener destroy;
};
static struct kde_input_manager *kde_input_manager = NULL;
static int current_state(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
CK(sd_bus_message_open_container(reply, 'a', "s"));
struct kde_input_manager *manager = userdata;
struct kde_input *input;
wl_list_for_each(input, &manager->inputs, link) {
CK(sd_bus_message_append_basic(reply, 's', input->sys_name));
}
CK(sd_bus_message_close_container(reply));
return 1;
}
static const sd_bus_vtable service_vtable[] = {
SD_BUS_VTABLE_START(0),
KDE_PROP("devicesSysNames", "as", current_state),
SD_BUS_VTABLE_END,
};
static int is_pointer(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
/**
* The client will differentiate between the pointer and the touchpad again,
* and here any pointer will be returned without distinguishing between the pointer and the
* touchpad.
*/
uint32_t is_pointer = input->input->prop.type == WLR_INPUT_DEVICE_POINTER;
return sd_bus_message_append_basic(reply, 'b', &is_pointer);
}
static int is_keyboard(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t is_keyboard = input->input->prop.type == WLR_INPUT_DEVICE_KEYBOARD;
return sd_bus_message_append_basic(reply, 'b', &is_keyboard);
}
static int is_touchpad(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t is_touchpad =
input->input->prop.type == WLR_INPUT_DEVICE_POINTER && input->input->prop.tap_finger_count;
return sd_bus_message_append_basic(reply, 'b', &is_touchpad);
}
static int is_touch(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t is_touch = input->input->prop.type == WLR_INPUT_DEVICE_TOUCH;
return sd_bus_message_append_basic(reply, 'b', &is_touch);
}
static int is_tablet_tool(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t is_tablet_tool = input->input->prop.type == WLR_INPUT_DEVICE_TABLET;
return sd_bus_message_append_basic(reply, 'b', &is_tablet_tool);
}
static int is_tablet_pad(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t is_tablet_pad = input->input->prop.type == WLR_INPUT_DEVICE_TABLET_PAD;
return sd_bus_message_append_basic(reply, 'b', &is_tablet_pad);
}
static int is_switch(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t is_switch = input->input->prop.type == WLR_INPUT_DEVICE_SWITCH;
return sd_bus_message_append_basic(reply, 'b', &is_switch);
}
static int name(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
return sd_bus_message_append_basic(reply, 's', input->input->name);
}
static int sys_name(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
return sd_bus_message_append_basic(reply, 's', input->sys_name);
}
static int product(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t product = input->input->prop.product;
return sd_bus_message_append_basic(reply, 'i', &product);
}
static int vendor(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t vendor = input->input->prop.vendor;
return sd_bus_message_append_basic(reply, 'i', &vendor);
}
static int supports_disable_events(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t event = input->input->prop.send_events_modes;
uint32_t enable = event & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
return sd_bus_message_append_basic(reply, 'b', &enable);
}
static int supports_disable_events_on_external_mouse(sd_bus *bus, const char *path,
const char *interface, const char *property,
sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t event = input->input->prop.send_events_modes;
uint32_t enable = event & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
return sd_bus_message_append_basic(reply, 'b', &enable);
}
static int get_touchpad_enable(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t mode = input->input->state.send_events_mode;
uint32_t enable = mode == LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
return sd_bus_message_append_basic(reply, 'b', &enable);
}
static int set_touchpad_enable(sd_bus *bus, const char *_path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
uint32_t touchpad_enable;
CK(sd_bus_message_read(reply, "b", &touchpad_enable));
struct kde_input *input = userdata;
struct input_state state = input->input->state;
state.send_events_mode = touchpad_enable
? LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
: LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
input_set_state(input->input, &state);
return sd_bus_reply_method_return(reply, NULL);
}
static int supports_pointer_acceleration(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply,
void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t acceleration = input->input->prop.has_pointer_accel;
return sd_bus_message_append_basic(reply, 'b', &acceleration);
}
static int default_pointer_acceleration(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
double acceleration = input->input->default_state.pointer_accel_speed;
return sd_bus_message_append_basic(reply, 'd', &acceleration);
}
static int get_accel_speed(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
double accel_speed = input->input->state.pointer_accel_speed;
return sd_bus_message_append_basic(reply, 'd', &accel_speed);
}
static int set_accel_speed(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
double accel_speed;
CK(sd_bus_message_read(reply, "d", &accel_speed));
struct kde_input *input = userdata;
struct input_state state = input->input->state;
state.pointer_accel_speed = accel_speed;
input_set_state(input->input, &state);
return sd_bus_reply_method_return(reply, NULL);
}
static int supports_pointer_acceleration_profile_adaptive(sd_bus *bus, const char *path,
const char *interface,
const char *property,
sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t acceleration =
input->input->prop.accel_profiles & LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
return sd_bus_message_append_basic(reply, 'b', &acceleration);
}
static int default_pointer_acceleration_profile_adaptive(sd_bus *bus, const char *path,
const char *interface,
const char *property,
sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t acceleration =
input->input->default_state.accel_profile & LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
return sd_bus_message_append_basic(reply, 'b', &acceleration);
}
static int get_accel_profile(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t accel_profile =
input->input->state.accel_profile & LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
return sd_bus_message_append_basic(reply, 'b', &accel_profile);
}
static int set_accel_profile(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
uint32_t accel_profile;
CK(sd_bus_message_read(reply, "b", &accel_profile));
struct kde_input *input = userdata;
struct input_state state = input->input->state;
state.accel_profile =
accel_profile ? LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE : LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT;
input_set_state(input->input, &state);
return sd_bus_reply_method_return(reply, NULL);
}
static int supports_left_handed(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t left_handed = input->input->prop.has_left_handed;
return sd_bus_message_append_basic(reply, 'b', &left_handed);
}
static int left_handed_enabled_by_default(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply,
void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t left_handed = input->input->default_state.left_handed;
return sd_bus_message_append_basic(reply, 'b', &left_handed);
}
static int get_left_handed(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t left_handed = input->input->state.left_handed;
return sd_bus_message_append_basic(reply, 'b', &left_handed);
}
static int set_left_handed(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
uint32_t left_handed;
CK(sd_bus_message_read(reply, "b", &left_handed));
struct kde_input *input = userdata;
struct input_state state = input->input->state;
state.left_handed = left_handed;
input_set_state(input->input, &state);
return sd_bus_reply_method_return(reply, NULL);
}
static int supports_natural_scroll(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t natural_scroll = input->input->prop.has_natural_scroll;
return sd_bus_message_append_basic(reply, 'b', &natural_scroll);
}
static int natural_scroll_enabled_by_default(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply,
void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t natural_scroll = input->input->default_state.natural_scroll;
return sd_bus_message_append_basic(reply, 'b', &natural_scroll);
}
static int get_natural_scroll(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t natural_scroll = input->input->state.natural_scroll;
return sd_bus_message_append_basic(reply, 'b', &natural_scroll);
}
static int set_natural_scroll(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
uint32_t natural_scroll;
CK(sd_bus_message_read(reply, "b", &natural_scroll));
struct kde_input *input = userdata;
struct input_state state = input->input->state;
state.natural_scroll = natural_scroll;
input_set_state(input->input, &state);
return sd_bus_reply_method_return(reply, NULL);
}
static int tap_finger_count(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t tap_finger_count = input->input->prop.tap_finger_count;
return sd_bus_message_append_basic(reply, 'i', &tap_finger_count);
}
static int tap_to_click_enabled_by_default(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply,
void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t tap_to_click = input->input->default_state.tap_to_click;
return sd_bus_message_append_basic(reply, 'b', &tap_to_click);
}
static int get_tap_click(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t tap_to_click = input->input->state.tap_to_click;
return sd_bus_message_append_basic(reply, 'b', &tap_to_click);
}
static int set_tap_click(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
uint32_t is_tap_click;
CK(sd_bus_message_read(reply, "b", &is_tap_click));
struct kde_input *input = userdata;
struct input_state state = input->input->state;
state.tap_to_click = is_tap_click;
input_set_state(input->input, &state);
return sd_bus_reply_method_return(reply, NULL);
}
static int supports_scroll_two_finger(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t scroll_methods = input->input->prop.scroll_methods;
uint32_t two_finger = scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG;
return sd_bus_message_append_basic(reply, 'b', &two_finger);
}
static int scroll_two_finger_enabled_by_default(sd_bus *bus, const char *path,
const char *interface, const char *property,
sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t scroll_methods = input->input->default_state.scroll_method;
uint32_t two_finger = scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG;
return sd_bus_message_append_basic(reply, 'b', &two_finger);
}
static int get_scroll_two_finger(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t scroll_methods = input->input->state.scroll_method;
uint32_t two_finger = scroll_methods & LIBINPUT_CONFIG_SCROLL_2FG;
return sd_bus_message_append_basic(reply, 'b', &two_finger);
}
static int set_scroll_two_finger(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
uint32_t method;
CK(sd_bus_message_read(reply, "b", &method));
struct kde_input *input = userdata;
int32_t current = input->input->state.scroll_method;
method = method ? LIBINPUT_CONFIG_SCROLL_2FG : LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
if ((current != LIBINPUT_CONFIG_SCROLL_2FG && method == LIBINPUT_CONFIG_SCROLL_2FG) ||
(current != LIBINPUT_CONFIG_SCROLL_EDGE && method == LIBINPUT_CONFIG_SCROLL_NO_SCROLL)) {
struct input_state state = input->input->state;
state.scroll_method = method;
input_set_state(input->input, &state);
}
return sd_bus_reply_method_return(reply, NULL);
}
static int supports_scroll_edge(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t scroll_methods = input->input->prop.scroll_methods;
uint32_t is_scroll_edge = scroll_methods & LIBINPUT_CONFIG_SCROLL_EDGE;
return sd_bus_message_append_basic(reply, 'b', &is_scroll_edge);
}
static int scroll_edge_enabled_by_default(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply,
void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t scroll_method = input->input->default_state.scroll_method;
uint32_t is_scroll_edge = scroll_method & LIBINPUT_CONFIG_SCROLL_EDGE;
return sd_bus_message_append_basic(reply, 'b', &is_scroll_edge);
}
static int get_scroll_edge(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t scroll_method = input->input->state.scroll_method;
uint32_t is_scroll_edge = scroll_method & LIBINPUT_CONFIG_SCROLL_EDGE;
return sd_bus_message_append_basic(reply, 'b', &is_scroll_edge);
}
static int set_scroll_edge(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
uint32_t method;
CK(sd_bus_message_read(reply, "b", &method));
struct kde_input *input = userdata;
uint32_t current = input->input->state.scroll_method;
method = method ? LIBINPUT_CONFIG_SCROLL_EDGE : LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
if ((current != LIBINPUT_CONFIG_SCROLL_EDGE && method == LIBINPUT_CONFIG_SCROLL_EDGE) ||
(current != LIBINPUT_CONFIG_SCROLL_2FG && method == LIBINPUT_CONFIG_SCROLL_NO_SCROLL)) {
struct input_state state = input->input->state;
state.scroll_method = method;
input_set_state(input->input, &state);
}
return sd_bus_reply_method_return(reply, NULL);
}
static int supports_disable_while_typing(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply,
void *userdata, sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t dwt = input->input->prop.has_dwt;
return sd_bus_message_append_basic(reply, 'b', &dwt);
}
static int disable_while_typing_enabled_by_default(sd_bus *bus, const char *path,
const char *interface, const char *property,
sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t dwt = input->input->default_state.dwt;
return sd_bus_message_append_basic(reply, 'b', &dwt);
}
static int get_disable_while_typing(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
uint32_t dwt = input->input->state.dwt;
return sd_bus_message_append_basic(reply, 'b', &dwt);
}
static int set_disable_while_typing(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
uint32_t dwt;
CK(sd_bus_message_read(reply, "b", &dwt));
struct kde_input *input = userdata;
struct input_state state = input->input->state;
state.dwt = dwt;
input_set_state(input->input, &state);
return sd_bus_reply_method_return(reply, NULL);
}
static int get_mapped_output(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct kde_input *input = userdata;
const char *mapped_to_output = input->input->state.mapped_to_output;
return sd_bus_message_append_basic(reply, 's', mapped_to_output ? mapped_to_output : "");
}
static int set_mapped_output(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
const char *output_name = NULL;
CK(sd_bus_message_read(reply, "s", &output_name));
bool none_output = !strcmp(output_name, "");
if (!none_output) {
struct kywc_output *kywc_output = kywc_output_by_name(output_name);
if (!kywc_output || !kywc_output->state.enabled) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid output or disabled.");
return sd_bus_reply_method_error(reply, &error);
}
}
struct kde_input *input = userdata;
const char *current = input->input->mapped_output ? input->input->state.mapped_to_output : NULL;
if (input->input->prop.support_mapped_to_output && (!current || strcmp(current, output_name))) {
struct input_state state = input->input->state;
state.mapped_to_output = none_output ? NULL : output_name;
input_set_state(input->input, &state);
}
return sd_bus_reply_method_return(reply, NULL);
}
static const sd_bus_vtable input_vtable[] = {
SD_BUS_VTABLE_START(0),
KDE_PROP("pointer", "b", is_pointer),
KDE_PROP("keyboard", "b", is_keyboard),
KDE_PROP("touchpad", "b", is_touchpad),
KDE_PROP("touch", "b", is_touch),
KDE_PROP("tabletTool", "b", is_tablet_tool),
KDE_PROP("tabletPad", "b", is_tablet_pad),
KDE_PROP("switchDevice", "b", is_switch),
KDE_PROP("name", "s", name),
KDE_PROP("sysName", "s", sys_name),
KDE_PROP("product", "i", product),
KDE_PROP("vendor", "i", vendor),
KDE_PROP("supportsDisableEvents", "b", supports_disable_events),
KDE_PROP("supportsDisableEventsOnExternalMouse", "b",
supports_disable_events_on_external_mouse),
KDE_WPROP("enabled", "b", get_touchpad_enable, set_touchpad_enable),
KDE_PROP("supportsPointerAcceleration", "b", supports_pointer_acceleration),
KDE_PROP("defaultPointerAcceleration", "d", default_pointer_acceleration),
KDE_WPROP("pointerAcceleration", "d", get_accel_speed, set_accel_speed),
KDE_PROP("supportsLeftHanded", "b", supports_left_handed),
KDE_PROP("leftHandedEnabledByDefault", "b", left_handed_enabled_by_default),
KDE_WPROP("leftHanded", "b", get_left_handed, set_left_handed),
KDE_PROP("supportsPointerAccelerationProfileAdaptive", "b",
supports_pointer_acceleration_profile_adaptive),
KDE_PROP("defaultPointerAccelerationProfileAdaptive", "b",
default_pointer_acceleration_profile_adaptive),
KDE_WPROP("pointerAccelerationProfileAdaptive", "b", get_accel_profile, set_accel_profile),
KDE_PROP("supportsNaturalScroll", "b", supports_natural_scroll),
KDE_PROP("naturalScrollEnabledByDefault", "b", natural_scroll_enabled_by_default),
KDE_WPROP("naturalScroll", "b", get_natural_scroll, set_natural_scroll),
KDE_PROP("tapFingerCount", "b", tap_finger_count),
KDE_PROP("tapToClickEnabledByDefault", "b", tap_to_click_enabled_by_default),
KDE_WPROP("tapToClick", "b", get_tap_click, set_tap_click),
KDE_PROP("supportsScrollTwoFinger", "b", supports_scroll_two_finger),
KDE_PROP("scrollTwoFingerEnabledByDefault", "b", scroll_two_finger_enabled_by_default),
KDE_WPROP("scrollTwoFinger", "b", get_scroll_two_finger, set_scroll_two_finger),
KDE_PROP("supportsScrollEdge", "b", supports_scroll_edge),
KDE_PROP("scrollEdgeEnabledByDefault", "b", scroll_edge_enabled_by_default),
KDE_WPROP("scrollEdge", "b", get_scroll_edge, set_scroll_edge),
KDE_PROP("supportsDisableWhileTyping", "b", supports_disable_while_typing),
KDE_PROP("disableWhileTypingEnabledByDefault", "b", disable_while_typing_enabled_by_default),
KDE_WPROP("disableWhileTyping", "b", get_disable_while_typing, set_disable_while_typing),
KDE_WPROP("outputName", "s", get_mapped_output, set_mapped_output),
SD_BUS_VTABLE_END,
};
static void kde_input_destroy(struct kde_input *input)
{
dbus_emit_signal(service_path, service_interface, "deviceRemoved", "s", input->sys_name);
wl_list_remove(&input->link);
wl_list_remove(&input->destroy.link);
dbus_unregister_object(input->dbus);
free(input->sys_name);
free(input);
}
static void handle_kde_input_destroy(struct wl_listener *listener, void *data)
{
struct kde_input *input = wl_container_of(listener, input, destroy);
kde_input_destroy(input);
}
static void handle_new_kde_input(struct wl_listener *listener, void *data)
{
struct input *input = data;
if (!input->device) {
return;
}
struct kde_input *kde_input = calloc(1, sizeof(*kde_input));
if (!kde_input) {
return;
}
wl_list_insert(&kde_input_manager->inputs, &kde_input->link);
kde_input->destroy.notify = handle_kde_input_destroy;
wl_signal_add(&input->events.destroy, &kde_input->destroy);
const char *sys_name = libinput_device_get_sysname(input->device);
kde_input->sys_name = strdup(sys_name);
kde_input->input = input;
size_t size = 1 + strlen(kde_input_path) + strlen(sys_name);
char *path = calloc(size, sizeof(char));
snprintf(path, size, "%s%s", kde_input_path, kde_input->sys_name);
kde_input->dbus =
dbus_register_object(NULL, path, kde_input_interface, input_vtable, kde_input);
free(path);
dbus_emit_signal(service_path, service_interface, "deviceAdded", "s", kde_input->sys_name);
}
static void handle_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&kde_input_manager->new_input.link);
wl_list_remove(&kde_input_manager->destroy.link);
/* destroy kde_input devices*/
struct kde_input *input, *input_tmp;
wl_list_for_each_safe(input, input_tmp, &kde_input_manager->inputs, link) {
kde_input_destroy(input);
}
free(kde_input_manager);
kde_input_manager = NULL;
}
bool kde_input_manager_create(struct config_manager *config_manager)
{
kde_input_manager = calloc(1, sizeof(*kde_input_manager));
if (!kde_input_manager) {
return false;
}
if (!dbus_register_object("org.kde.KWin", service_path, service_interface, service_vtable,
kde_input_manager)) {
free(kde_input_manager);
kde_input_manager = NULL;
return false;
}
wl_list_init(&kde_input_manager->inputs);
kde_input_manager->new_input.notify = handle_new_kde_input;
input_add_new_listener(&kde_input_manager->new_input);
kde_input_manager->destroy.notify = handle_destroy;
server_add_destroy_listener(config_manager->server, &kde_input_manager->destroy);
return true;
}
kylin-wayland-compositor/src/config/config_p.h 0000664 0001750 0001750 00000003317 15160460057 020460 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#ifndef _CONFIG_P_H_
#define _CONFIG_P_H_
#include "config.h"
struct config_manager {
struct server *server;
struct wl_list configs;
const char *file;
json_object *json; /* user config */
json_object *sys_json; /* system default config */
struct wl_listener server_ready;
struct wl_listener server_destroy;
};
bool config_manager_common_init(struct config_manager *config_manager);
#if HAVE_KDE_GLOBAL_ACCEL
bool kde_global_accel_manager_create(struct config_manager *config_manager);
#else
static __attribute__((unused)) inline bool
kde_global_accel_manager_create(struct config_manager *config_manager)
{
return false;
}
#endif
#if HAVE_KDE_INPUT
bool kde_input_manager_create(struct config_manager *config_manager);
#else
static __attribute__((unused)) inline bool
kde_input_manager_create(struct config_manager *config_manager)
{
return false;
}
#endif
#if HAVE_UKUI_SHORTCUT
bool ukui_shortcut_manager_create(struct config_manager *config_manager);
#else
static __attribute__((unused)) inline bool
ukui_shortcut_manager_create(struct config_manager *config_manager)
{
return false;
}
#endif
#if HAVE_UKUI_GSETTINGS
bool ukui_gsettings_create(struct config_manager *config_manager);
#else
static __attribute__((unused)) inline bool
ukui_gsettings_create(struct config_manager *config_manager)
{
return false;
}
#endif
#if HAVE_UKUI_VIEW_MODE
bool ukui_view_mode_manager_create(struct config_manager *config_manager);
#else
static __attribute__((unused)) inline bool
ukui_view_mode_manager_create(struct config_manager *config_manager)
{
return false;
}
#endif
#endif /* _CONFIG_P_H_ */
kylin-wayland-compositor/src/config/qtkey_mapper.c 0000664 0001750 0001750 00000102713 15160460057 021370 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
enum QtKey {
Key_Escape = 0x01000000, // misc keys
Key_Tab = 0x01000001,
Key_Backtab = 0x01000002,
Key_Backspace = 0x01000003,
Key_Return = 0x01000004,
Key_Enter = 0x01000005,
Key_Insert = 0x01000006,
Key_Delete = 0x01000007,
Key_Pause = 0x01000008,
Key_Print = 0x01000009, // print screen
Key_SysReq = 0x0100000a,
Key_Clear = 0x0100000b,
Key_Home = 0x01000010, // cursor movement
Key_End = 0x01000011,
Key_Left = 0x01000012,
Key_Up = 0x01000013,
Key_Right = 0x01000014,
Key_Down = 0x01000015,
Key_PageUp = 0x01000016,
Key_PageDown = 0x01000017,
Key_Shift = 0x01000020, // modifiers
Key_Control = 0x01000021,
Key_Meta = 0x01000022,
Key_Alt = 0x01000023,
Key_CapsLock = 0x01000024,
Key_NumLock = 0x01000025,
Key_ScrollLock = 0x01000026,
Key_F1 = 0x01000030, // function keys
Key_F2 = 0x01000031,
Key_F3 = 0x01000032,
Key_F4 = 0x01000033,
Key_F5 = 0x01000034,
Key_F6 = 0x01000035,
Key_F7 = 0x01000036,
Key_F8 = 0x01000037,
Key_F9 = 0x01000038,
Key_F10 = 0x01000039,
Key_F11 = 0x0100003a,
Key_F12 = 0x0100003b,
Key_F13 = 0x0100003c,
Key_F14 = 0x0100003d,
Key_F15 = 0x0100003e,
Key_F16 = 0x0100003f,
Key_F17 = 0x01000040,
Key_F18 = 0x01000041,
Key_F19 = 0x01000042,
Key_F20 = 0x01000043,
Key_F21 = 0x01000044,
Key_F22 = 0x01000045,
Key_F23 = 0x01000046,
Key_F24 = 0x01000047,
Key_F25 = 0x01000048, // F25 .. F35 only on X11
Key_F26 = 0x01000049,
Key_F27 = 0x0100004a,
Key_F28 = 0x0100004b,
Key_F29 = 0x0100004c,
Key_F30 = 0x0100004d,
Key_F31 = 0x0100004e,
Key_F32 = 0x0100004f,
Key_F33 = 0x01000050,
Key_F34 = 0x01000051,
Key_F35 = 0x01000052,
Key_Super_L = 0x01000053, // extra keys
Key_Super_R = 0x01000054,
Key_Menu = 0x01000055,
Key_Hyper_L = 0x01000056,
Key_Hyper_R = 0x01000057,
Key_Help = 0x01000058,
Key_Direction_L = 0x01000059,
Key_Direction_R = 0x01000060,
Key_Space = 0x20, // 7 bit printable ASCII
Key_Any = Key_Space,
Key_Exclam = 0x21,
Key_QuoteDbl = 0x22,
Key_NumberSign = 0x23,
Key_Dollar = 0x24,
Key_Percent = 0x25,
Key_Ampersand = 0x26,
Key_Apostrophe = 0x27,
Key_ParenLeft = 0x28,
Key_ParenRight = 0x29,
Key_Asterisk = 0x2a,
Key_Plus = 0x2b,
Key_Comma = 0x2c,
Key_Minus = 0x2d,
Key_Period = 0x2e,
Key_Slash = 0x2f,
Key_0 = 0x30,
Key_1 = 0x31,
Key_2 = 0x32,
Key_3 = 0x33,
Key_4 = 0x34,
Key_5 = 0x35,
Key_6 = 0x36,
Key_7 = 0x37,
Key_8 = 0x38,
Key_9 = 0x39,
Key_Colon = 0x3a,
Key_Semicolon = 0x3b,
Key_Less = 0x3c,
Key_Equal = 0x3d,
Key_Greater = 0x3e,
Key_Question = 0x3f,
Key_At = 0x40,
Key_A = 0x41,
Key_B = 0x42,
Key_C = 0x43,
Key_D = 0x44,
Key_E = 0x45,
Key_F = 0x46,
Key_G = 0x47,
Key_H = 0x48,
Key_I = 0x49,
Key_J = 0x4a,
Key_K = 0x4b,
Key_L = 0x4c,
Key_M = 0x4d,
Key_N = 0x4e,
Key_O = 0x4f,
Key_P = 0x50,
Key_Q = 0x51,
Key_R = 0x52,
Key_S = 0x53,
Key_T = 0x54,
Key_U = 0x55,
Key_V = 0x56,
Key_W = 0x57,
Key_X = 0x58,
Key_Y = 0x59,
Key_Z = 0x5a,
Key_BracketLeft = 0x5b,
Key_Backslash = 0x5c,
Key_BracketRight = 0x5d,
Key_AsciiCircum = 0x5e,
Key_Underscore = 0x5f,
Key_QuoteLeft = 0x60,
Key_BraceLeft = 0x7b,
Key_Bar = 0x7c,
Key_BraceRight = 0x7d,
Key_AsciiTilde = 0x7e,
Key_nobreakspace = 0x0a0,
Key_exclamdown = 0x0a1,
Key_cent = 0x0a2,
Key_sterling = 0x0a3,
Key_currency = 0x0a4,
Key_yen = 0x0a5,
Key_brokenbar = 0x0a6,
Key_section = 0x0a7,
Key_diaeresis = 0x0a8,
Key_copyright = 0x0a9,
Key_ordfeminine = 0x0aa,
Key_guillemotleft = 0x0ab, // left angle quotation mark
Key_notsign = 0x0ac,
Key_hyphen = 0x0ad,
Key_registered = 0x0ae,
Key_macron = 0x0af,
Key_degree = 0x0b0,
Key_plusminus = 0x0b1,
Key_twosuperior = 0x0b2,
Key_threesuperior = 0x0b3,
Key_acute = 0x0b4,
Key_mu = 0x0b5,
Key_paragraph = 0x0b6,
Key_periodcentered = 0x0b7,
Key_cedilla = 0x0b8,
Key_onesuperior = 0x0b9,
Key_masculine = 0x0ba,
Key_guillemotright = 0x0bb, // right angle quotation mark
Key_onequarter = 0x0bc,
Key_onehalf = 0x0bd,
Key_threequarters = 0x0be,
Key_questiondown = 0x0bf,
Key_Agrave = 0x0c0,
Key_Aacute = 0x0c1,
Key_Acircumflex = 0x0c2,
Key_Atilde = 0x0c3,
Key_Adiaeresis = 0x0c4,
Key_Aring = 0x0c5,
Key_AE = 0x0c6,
Key_Ccedilla = 0x0c7,
Key_Egrave = 0x0c8,
Key_Eacute = 0x0c9,
Key_Ecircumflex = 0x0ca,
Key_Ediaeresis = 0x0cb,
Key_Igrave = 0x0cc,
Key_Iacute = 0x0cd,
Key_Icircumflex = 0x0ce,
Key_Idiaeresis = 0x0cf,
Key_ETH = 0x0d0,
Key_Ntilde = 0x0d1,
Key_Ograve = 0x0d2,
Key_Oacute = 0x0d3,
Key_Ocircumflex = 0x0d4,
Key_Otilde = 0x0d5,
Key_Odiaeresis = 0x0d6,
Key_multiply = 0x0d7,
Key_Ooblique = 0x0d8,
Key_Ugrave = 0x0d9,
Key_Uacute = 0x0da,
Key_Ucircumflex = 0x0db,
Key_Udiaeresis = 0x0dc,
Key_Yacute = 0x0dd,
Key_THORN = 0x0de,
Key_ssharp = 0x0df,
Key_division = 0x0f7,
Key_ydiaeresis = 0x0ff,
// International input method support (X keycode - 0xEE00, the
// definition follows Qt/Embedded 2.3.7) Only interesting if
// you are writing your own input method
// International & multi-key character composition
Key_AltGr = 0x01001103,
Key_Multi_key = 0x01001120, // Multi-key character compose
Key_Codeinput = 0x01001137,
Key_SingleCandidate = 0x0100113c,
Key_MultipleCandidate = 0x0100113d,
Key_PreviousCandidate = 0x0100113e,
// Misc Functions
Key_Mode_switch = 0x0100117e, // Character set switch
// Key_script_switch = 0x0100117e, // Alias for mode_switch
// Japanese keyboard support
Key_Kanji = 0x01001121, // Kanji, Kanji convert
Key_Muhenkan = 0x01001122, // Cancel Conversion
// Key_Henkan_Mode = 0x01001123, // Start/Stop Conversion
Key_Henkan = 0x01001123, // Alias for Henkan_Mode
Key_Romaji = 0x01001124, // to Romaji
Key_Hiragana = 0x01001125, // to Hiragana
Key_Katakana = 0x01001126, // to Katakana
Key_Hiragana_Katakana = 0x01001127, // Hiragana/Katakana toggle
Key_Zenkaku = 0x01001128, // to Zenkaku
Key_Hankaku = 0x01001129, // to Hankaku
Key_Zenkaku_Hankaku = 0x0100112a, // Zenkaku/Hankaku toggle
Key_Touroku = 0x0100112b, // Add to Dictionary
Key_Massyo = 0x0100112c, // Delete from Dictionary
Key_Kana_Lock = 0x0100112d, // Kana Lock
Key_Kana_Shift = 0x0100112e, // Kana Shift
Key_Eisu_Shift = 0x0100112f, // Alphanumeric Shift
Key_Eisu_toggle = 0x01001130, // Alphanumeric toggle
// Key_Kanji_Bangou = 0x01001137, // Codeinput
// Key_Zen_Koho = 0x0100113d, // Multiple/All Candidate(s)
// Key_Mae_Koho = 0x0100113e, // Previous Candidate
// Korean keyboard support
//
// In fact, many Korean users need only 2 keys, Key_Hangul and
// Key_Hangul_Hanja. But rest of the keys are good for future.
Key_Hangul = 0x01001131, // Hangul start/stop(toggle)
Key_Hangul_Start = 0x01001132, // Hangul start
Key_Hangul_End = 0x01001133, // Hangul end, English start
Key_Hangul_Hanja = 0x01001134, // Start Hangul->Hanja Conversion
Key_Hangul_Jamo = 0x01001135, // Hangul Jamo mode
Key_Hangul_Romaja = 0x01001136, // Hangul Romaja mode
// Key_Hangul_Codeinput = 0x01001137, // Hangul code input mode
Key_Hangul_Jeonja = 0x01001138, // Jeonja mode
Key_Hangul_Banja = 0x01001139, // Banja mode
Key_Hangul_PreHanja = 0x0100113a, // Pre Hanja conversion
Key_Hangul_PostHanja = 0x0100113b, // Post Hanja conversion
// Key_Hangul_SingleCandidate = 0x0100113c, // Single candidate
// Key_Hangul_MultipleCandidate = 0x0100113d, // Multiple candidate
// Key_Hangul_PreviousCandidate = 0x0100113e, // Previous candidate
Key_Hangul_Special = 0x0100113f, // Special symbols
// Key_Hangul_switch = 0x0100117e, // Alias for mode_switch
// dead keys (X keycode - 0xED00 to avoid the conflict)
Key_Dead_Grave = 0x01001250,
Key_Dead_Acute = 0x01001251,
Key_Dead_Circumflex = 0x01001252,
Key_Dead_Tilde = 0x01001253,
Key_Dead_Macron = 0x01001254,
Key_Dead_Breve = 0x01001255,
Key_Dead_Abovedot = 0x01001256,
Key_Dead_Diaeresis = 0x01001257,
Key_Dead_Abovering = 0x01001258,
Key_Dead_Doubleacute = 0x01001259,
Key_Dead_Caron = 0x0100125a,
Key_Dead_Cedilla = 0x0100125b,
Key_Dead_Ogonek = 0x0100125c,
Key_Dead_Iota = 0x0100125d,
Key_Dead_Voiced_Sound = 0x0100125e,
Key_Dead_Semivoiced_Sound = 0x0100125f,
Key_Dead_Belowdot = 0x01001260,
Key_Dead_Hook = 0x01001261,
Key_Dead_Horn = 0x01001262,
Key_Dead_Stroke = 0x01001263,
Key_Dead_Abovecomma = 0x01001264,
Key_Dead_Abovereversedcomma = 0x01001265,
Key_Dead_Doublegrave = 0x01001266,
Key_Dead_Belowring = 0x01001267,
Key_Dead_Belowmacron = 0x01001268,
Key_Dead_Belowcircumflex = 0x01001269,
Key_Dead_Belowtilde = 0x0100126a,
Key_Dead_Belowbreve = 0x0100126b,
Key_Dead_Belowdiaeresis = 0x0100126c,
Key_Dead_Invertedbreve = 0x0100126d,
Key_Dead_Belowcomma = 0x0100126e,
Key_Dead_Currency = 0x0100126f,
Key_Dead_a = 0x01001280,
Key_Dead_A = 0x01001281,
Key_Dead_e = 0x01001282,
Key_Dead_E = 0x01001283,
Key_Dead_i = 0x01001284,
Key_Dead_I = 0x01001285,
Key_Dead_o = 0x01001286,
Key_Dead_O = 0x01001287,
Key_Dead_u = 0x01001288,
Key_Dead_U = 0x01001289,
Key_Dead_Small_Schwa = 0x0100128a,
Key_Dead_Capital_Schwa = 0x0100128b,
Key_Dead_Greek = 0x0100128c,
Key_Dead_Lowline = 0x01001290,
Key_Dead_Aboveverticalline = 0x01001291,
Key_Dead_Belowverticalline = 0x01001292,
Key_Dead_Longsolidusoverlay = 0x01001293,
// multimedia/internet keys - ignored by default - see QKeyEvent c'tor
Key_Back = 0x01000061,
Key_Forward = 0x01000062,
Key_Stop = 0x01000063,
Key_Refresh = 0x01000064,
Key_VolumeDown = 0x01000070,
Key_VolumeMute = 0x01000071,
Key_VolumeUp = 0x01000072,
Key_BassBoost = 0x01000073,
Key_BassUp = 0x01000074,
Key_BassDown = 0x01000075,
Key_TrebleUp = 0x01000076,
Key_TrebleDown = 0x01000077,
Key_MediaPlay = 0x01000080,
Key_MediaStop = 0x01000081,
Key_MediaPrevious = 0x01000082,
Key_MediaNext = 0x01000083,
Key_MediaRecord = 0x01000084,
Key_MediaPause = 0x1000085,
Key_MediaTogglePlayPause = 0x1000086,
Key_HomePage = 0x01000090,
Key_Favorites = 0x01000091,
Key_Search = 0x01000092,
Key_Standby = 0x01000093,
Key_OpenUrl = 0x01000094,
Key_LaunchMail = 0x010000a0,
Key_LaunchMedia = 0x010000a1,
Key_Launch0 = 0x010000a2,
Key_Launch1 = 0x010000a3,
Key_Launch2 = 0x010000a4,
Key_Launch3 = 0x010000a5,
Key_Launch4 = 0x010000a6,
Key_Launch5 = 0x010000a7,
Key_Launch6 = 0x010000a8,
Key_Launch7 = 0x010000a9,
Key_Launch8 = 0x010000aa,
Key_Launch9 = 0x010000ab,
Key_LaunchA = 0x010000ac,
Key_LaunchB = 0x010000ad,
Key_LaunchC = 0x010000ae,
Key_LaunchD = 0x010000af,
Key_LaunchE = 0x010000b0,
Key_LaunchF = 0x010000b1,
Key_MonBrightnessUp = 0x010000b2,
Key_MonBrightnessDown = 0x010000b3,
Key_KeyboardLightOnOff = 0x010000b4,
Key_KeyboardBrightnessUp = 0x010000b5,
Key_KeyboardBrightnessDown = 0x010000b6,
Key_PowerOff = 0x010000b7,
Key_WakeUp = 0x010000b8,
Key_Eject = 0x010000b9,
Key_ScreenSaver = 0x010000ba,
Key_WWW = 0x010000bb,
Key_Memo = 0x010000bc,
Key_LightBulb = 0x010000bd,
Key_Shop = 0x010000be,
Key_History = 0x010000bf,
Key_AddFavorite = 0x010000c0,
Key_HotLinks = 0x010000c1,
Key_BrightnessAdjust = 0x010000c2,
Key_Finance = 0x010000c3,
Key_Community = 0x010000c4,
Key_AudioRewind = 0x010000c5, // Media rewind
Key_BackForward = 0x010000c6,
Key_ApplicationLeft = 0x010000c7,
Key_ApplicationRight = 0x010000c8,
Key_Book = 0x010000c9,
Key_CD = 0x010000ca,
Key_Calculator = 0x010000cb,
Key_ToDoList = 0x010000cc,
Key_ClearGrab = 0x010000cd,
Key_Close = 0x010000ce,
Key_Copy = 0x010000cf,
Key_Cut = 0x010000d0,
Key_Display = 0x010000d1, // Output switch key
Key_DOS = 0x010000d2,
Key_Documents = 0x010000d3,
Key_Excel = 0x010000d4,
Key_Explorer = 0x010000d5,
Key_Game = 0x010000d6,
Key_Go = 0x010000d7,
Key_iTouch = 0x010000d8,
Key_LogOff = 0x010000d9,
Key_Market = 0x010000da,
Key_Meeting = 0x010000db,
Key_MenuKB = 0x010000dc,
Key_MenuPB = 0x010000dd,
Key_MySites = 0x010000de,
Key_News = 0x010000df,
Key_OfficeHome = 0x010000e0,
Key_Option = 0x010000e1,
Key_Paste = 0x010000e2,
Key_Phone = 0x010000e3,
Key_Calendar = 0x010000e4,
Key_Reply = 0x010000e5,
Key_Reload = 0x010000e6,
Key_RotateWindows = 0x010000e7,
Key_RotationPB = 0x010000e8,
Key_RotationKB = 0x010000e9,
Key_Save = 0x010000ea,
Key_Send = 0x010000eb,
Key_Spell = 0x010000ec,
Key_SplitScreen = 0x010000ed,
Key_Support = 0x010000ee,
Key_TaskPane = 0x010000ef,
Key_Terminal = 0x010000f0,
Key_Tools = 0x010000f1,
Key_Travel = 0x010000f2,
Key_Video = 0x010000f3,
Key_Word = 0x010000f4,
Key_Xfer = 0x010000f5,
Key_ZoomIn = 0x010000f6,
Key_ZoomOut = 0x010000f7,
Key_Away = 0x010000f8,
Key_Messenger = 0x010000f9,
Key_WebCam = 0x010000fa,
Key_MailForward = 0x010000fb,
Key_Pictures = 0x010000fc,
Key_Music = 0x010000fd,
Key_Battery = 0x010000fe,
Key_Bluetooth = 0x010000ff,
Key_WLAN = 0x01000100,
Key_UWB = 0x01000101,
Key_AudioForward = 0x01000102, // Media fast-forward
Key_AudioRepeat = 0x01000103, // Toggle repeat mode
Key_AudioRandomPlay = 0x01000104, // Toggle shuffle mode
Key_Subtitle = 0x01000105,
Key_AudioCycleTrack = 0x01000106,
Key_Time = 0x01000107,
Key_Hibernate = 0x01000108,
Key_View = 0x01000109,
Key_TopMenu = 0x0100010a,
Key_PowerDown = 0x0100010b,
Key_Suspend = 0x0100010c,
Key_ContrastAdjust = 0x0100010d,
Key_LaunchG = 0x0100010e,
Key_LaunchH = 0x0100010f,
Key_TouchpadToggle = 0x01000110,
Key_TouchpadOn = 0x01000111,
Key_TouchpadOff = 0x01000112,
Key_MicMute = 0x01000113,
Key_Red = 0x01000114,
Key_Green = 0x01000115,
Key_Yellow = 0x01000116,
Key_Blue = 0x01000117,
Key_ChannelUp = 0x01000118,
Key_ChannelDown = 0x01000119,
Key_Guide = 0x0100011a,
Key_Info = 0x0100011b,
Key_Settings = 0x0100011c,
Key_MicVolumeUp = 0x0100011d,
Key_MicVolumeDown = 0x0100011e,
Key_New = 0x01000120,
Key_Open = 0x01000121,
Key_Find = 0x01000122,
Key_Undo = 0x01000123,
Key_Redo = 0x01000124,
Key_RFKill = 0x01000125,
Key_MediaLast = 0x0100ffff,
// Keypad navigation keys
Key_Select = 0x01010000,
Key_Yes = 0x01010001,
Key_No = 0x01010002,
// Newer misc keys
Key_Cancel = 0x01020001,
Key_Printer = 0x01020002,
Key_Execute = 0x01020003,
Key_Sleep = 0x01020004,
Key_Play = 0x01020005, // Not the same as Key_MediaPlay
Key_Zoom = 0x01020006,
// Key_Jisho = 0x01020007, // IME: Dictionary key
// Key_Oyayubi_Left = 0x01020008, // IME: Left Oyayubi key
// Key_Oyayubi_Right = 0x01020009, // IME: Right Oyayubi key
Key_Exit = 0x0102000a,
// Device keys
Key_Context1 = 0x01100000,
Key_Context2 = 0x01100001,
Key_Context3 = 0x01100002,
Key_Context4 = 0x01100003,
Key_Call = 0x01100004, // set absolute state to in a call (do not toggle state)
Key_Hangup = 0x01100005, // set absolute state to hang up (do not toggle state)
Key_Flip = 0x01100006,
Key_ToggleCallHangup = 0x01100007, // a toggle key for answering, or hanging up
Key_VoiceDial = 0x01100008,
Key_LastNumberRedial = 0x01100009,
Key_Camera = 0x01100020,
Key_CameraFocus = 0x01100021,
Key_unknown = 0x01ffffff
};
static struct Xkb2Qt {
uint32_t keysym;
enum QtKey qtkey;
} KeyTbl[] = {
// misc keys
{ XKB_KEY_Escape, Key_Escape },
{ XKB_KEY_Tab, Key_Tab },
{ XKB_KEY_ISO_Left_Tab, Key_Backtab },
{ XKB_KEY_BackSpace, Key_Backspace },
{ XKB_KEY_Return, Key_Return },
{ XKB_KEY_Insert, Key_Insert },
{ XKB_KEY_Delete, Key_Delete },
// { XKB_KEY_Clear, Key_Delete },
{ XKB_KEY_Pause, Key_Pause },
{ XKB_KEY_Print, Key_Print },
{ 0x1005FF60, Key_SysReq }, // hardcoded Sun SysReq
// { 0x1007ff00, Key_SysReq }, // hardcoded X386 SysReq
// cursor movement
{ XKB_KEY_Home, Key_Home },
{ XKB_KEY_End, Key_End },
{ XKB_KEY_Left, Key_Left },
{ XKB_KEY_Up, Key_Up },
{ XKB_KEY_Right, Key_Right },
{ XKB_KEY_Down, Key_Down },
{ XKB_KEY_Prior, Key_PageUp },
{ XKB_KEY_Next, Key_PageDown },
// modifiers
// { XKB_KEY_Shift_L, Key_Shift },
// { XKB_KEY_Shift_R, Key_Shift },
// { XKB_KEY_Shift_Lock, Key_Shift },
// { XKB_KEY_Control_L, Key_Control },
// { XKB_KEY_Control_R, Key_Control },
// { XKB_KEY_Meta_L, Key_Meta },
// { XKB_KEY_Meta_R, Key_Meta },
// { XKB_KEY_Alt_L, Key_Alt },
// { XKB_KEY_Alt_R, Key_Alt },
{ XKB_KEY_Caps_Lock, Key_CapsLock },
{ XKB_KEY_Num_Lock, Key_NumLock },
{ XKB_KEY_Scroll_Lock, Key_ScrollLock },
{ XKB_KEY_Super_L, Key_Super_L },
{ XKB_KEY_Super_R, Key_Super_R },
{ XKB_KEY_Menu, Key_Menu },
{ XKB_KEY_Hyper_L, Key_Hyper_L },
{ XKB_KEY_Hyper_R, Key_Hyper_R },
{ XKB_KEY_Help, Key_Help },
// { 0x1000FF74, Key_Backtab }, // hardcoded HP backtab
{ 0x1005FF10, Key_F11 }, // hardcoded Sun F36 (labeled F11)
{ 0x1005FF11, Key_F12 }, // hardcoded Sun F37 (labeled F12)
// numeric and function keypad keys
// { XKB_KEY_KP_Space, Key_Space },
// { XKB_KEY_KP_Tab, Key_Tab },
// { XKB_KEY_KP_Enter, Key_Enter },
// { XKB_KEY_KP_Home, Key_Home },
// { XKB_KEY_KP_Left, Key_Left },
// { XKB_KEY_KP_Up, Key_Up },
// { XKB_KEY_KP_Right, Key_Right },
// { XKB_KEY_KP_Down, Key_Down },
// { XKB_KEY_KP_Prior, Key_PageUp },
// { XKB_KEY_KP_Next, Key_PageDown },
// { XKB_KEY_KP_End, Key_End },
// { XKB_KEY_KP_Begin, Key_Clear },
// { XKB_KEY_KP_Insert, Key_Insert },
// { XKB_KEY_KP_Delete, Key_Delete },
// { XKB_KEY_KP_Equal, Key_Equal },
// { XKB_KEY_KP_Multiply, Key_Asterisk },
// { XKB_KEY_KP_Add, Key_Plus },
// { XKB_KEY_KP_Separator, Key_Comma },
// { XKB_KEY_KP_Subtract, Key_Minus },
// { XKB_KEY_KP_Decimal, Key_Period },
// { XKB_KEY_KP_Divide, Key_Slash },
// special non-XF86 function keys
{ XKB_KEY_Undo, Key_Undo },
{ XKB_KEY_Redo, Key_Redo },
{ XKB_KEY_Find, Key_Find },
{ XKB_KEY_Cancel, Key_Cancel },
// International input method support keys
// International & multi-key character composition
{ XKB_KEY_ISO_Level3_Shift, Key_AltGr },
{ XKB_KEY_Multi_key, Key_Multi_key },
{ XKB_KEY_Codeinput, Key_Codeinput },
{ XKB_KEY_SingleCandidate, Key_SingleCandidate },
{ XKB_KEY_MultipleCandidate, Key_MultipleCandidate },
{ XKB_KEY_PreviousCandidate, Key_PreviousCandidate },
// Misc Functions
{ XKB_KEY_Mode_switch, Key_Mode_switch },
// { XKB_KEY_script_switch, Key_Mode_switch },
// Japanese keyboard support
{ XKB_KEY_Kanji, Key_Kanji },
{ XKB_KEY_Muhenkan, Key_Muhenkan },
// { XKB_KEY_Henkan_Mode, Key_Henkan_Mode},
{ XKB_KEY_Henkan_Mode, Key_Henkan },
// { XKB_KEY_Henkan, Key_Henkan },
{ XKB_KEY_Romaji, Key_Romaji },
{ XKB_KEY_Hiragana, Key_Hiragana },
{ XKB_KEY_Katakana, Key_Katakana },
{ XKB_KEY_Hiragana_Katakana, Key_Hiragana_Katakana },
{ XKB_KEY_Zenkaku, Key_Zenkaku },
{ XKB_KEY_Hankaku, Key_Hankaku },
{ XKB_KEY_Zenkaku_Hankaku, Key_Zenkaku_Hankaku },
{ XKB_KEY_Touroku, Key_Touroku },
{ XKB_KEY_Massyo, Key_Massyo },
{ XKB_KEY_Kana_Lock, Key_Kana_Lock },
{ XKB_KEY_Kana_Shift, Key_Kana_Shift },
{ XKB_KEY_Eisu_Shift, Key_Eisu_Shift },
{ XKB_KEY_Eisu_toggle, Key_Eisu_toggle },
// { XKB_KEY_Kanji_Bangou, Key_Kanji_Bangou},
// { XKB_KEY_Zen_Koho, Key_Zen_Koho},
// { XKB_KEY_Mae_Koho, Key_Mae_Koho},
// { XKB_KEY_Kanji_Bangou, Key_Codeinput },
// { XKB_KEY_Zen_Koho, Key_MultipleCandidate },
// { XKB_KEY_Mae_Koho, Key_PreviousCandidate },
// Korean keyboard support
{ XKB_KEY_Hangul, Key_Hangul },
{ XKB_KEY_Hangul_Start, Key_Hangul_Start },
{ XKB_KEY_Hangul_End, Key_Hangul_End },
{ XKB_KEY_Hangul_Hanja, Key_Hangul_Hanja },
{ XKB_KEY_Hangul_Jamo, Key_Hangul_Jamo },
{ XKB_KEY_Hangul_Romaja, Key_Hangul_Romaja },
// { XKB_KEY_Hangul_Codeinput, Key_Hangul_Codeinput},
// { XKB_KEY_Hangul_Codeinput, Key_Codeinput },
{ XKB_KEY_Hangul_Jeonja, Key_Hangul_Jeonja },
{ XKB_KEY_Hangul_Banja, Key_Hangul_Banja },
{ XKB_KEY_Hangul_PreHanja, Key_Hangul_PreHanja },
{ XKB_KEY_Hangul_PostHanja, Key_Hangul_PostHanja },
// { XKB_KEY_Hangul_SingleCandidate,Key_Hangul_SingleCandidate},
// { XKB_KEY_Hangul_MultipleCandidate,Key_Hangul_MultipleCandidate},
// { XKB_KEY_Hangul_PreviousCandidate,Key_Hangul_PreviousCandidate},
// { XKB_KEY_Hangul_SingleCandidate, Key_SingleCandidate },
// { XKB_KEY_Hangul_MultipleCandidate, Key_MultipleCandidate },
// { XKB_KEY_Hangul_PreviousCandidate, Key_PreviousCandidate },
{ XKB_KEY_Hangul_Special, Key_Hangul_Special },
// { XKB_KEY_Hangul_switch, Key_Hangul_switch},
// { XKB_KEY_Hangul_switch, Key_Mode_switch },
// dead keys
{ XKB_KEY_dead_grave, Key_Dead_Grave },
{ XKB_KEY_dead_acute, Key_Dead_Acute },
{ XKB_KEY_dead_circumflex, Key_Dead_Circumflex },
{ XKB_KEY_dead_tilde, Key_Dead_Tilde },
{ XKB_KEY_dead_macron, Key_Dead_Macron },
{ XKB_KEY_dead_breve, Key_Dead_Breve },
{ XKB_KEY_dead_abovedot, Key_Dead_Abovedot },
{ XKB_KEY_dead_diaeresis, Key_Dead_Diaeresis },
{ XKB_KEY_dead_abovering, Key_Dead_Abovering },
{ XKB_KEY_dead_doubleacute, Key_Dead_Doubleacute },
{ XKB_KEY_dead_caron, Key_Dead_Caron },
{ XKB_KEY_dead_cedilla, Key_Dead_Cedilla },
{ XKB_KEY_dead_ogonek, Key_Dead_Ogonek },
{ XKB_KEY_dead_iota, Key_Dead_Iota },
{ XKB_KEY_dead_voiced_sound, Key_Dead_Voiced_Sound },
{ XKB_KEY_dead_semivoiced_sound, Key_Dead_Semivoiced_Sound },
{ XKB_KEY_dead_belowdot, Key_Dead_Belowdot },
{ XKB_KEY_dead_hook, Key_Dead_Hook },
{ XKB_KEY_dead_horn, Key_Dead_Horn },
{ XKB_KEY_dead_stroke, Key_Dead_Stroke },
{ XKB_KEY_dead_abovecomma, Key_Dead_Abovecomma },
{ XKB_KEY_dead_abovereversedcomma, Key_Dead_Abovereversedcomma },
{ XKB_KEY_dead_doublegrave, Key_Dead_Doublegrave },
{ XKB_KEY_dead_belowring, Key_Dead_Belowring },
{ XKB_KEY_dead_belowmacron, Key_Dead_Belowmacron },
{ XKB_KEY_dead_belowcircumflex, Key_Dead_Belowcircumflex },
{ XKB_KEY_dead_belowtilde, Key_Dead_Belowtilde },
{ XKB_KEY_dead_belowbreve, Key_Dead_Belowbreve },
{ XKB_KEY_dead_belowdiaeresis, Key_Dead_Belowdiaeresis },
{ XKB_KEY_dead_invertedbreve, Key_Dead_Invertedbreve },
{ XKB_KEY_dead_belowcomma, Key_Dead_Belowcomma },
{ XKB_KEY_dead_currency, Key_Dead_Currency },
{ XKB_KEY_dead_a, Key_Dead_a },
{ XKB_KEY_dead_A, Key_Dead_A },
{ XKB_KEY_dead_e, Key_Dead_e },
{ XKB_KEY_dead_E, Key_Dead_E },
{ XKB_KEY_dead_i, Key_Dead_i },
{ XKB_KEY_dead_I, Key_Dead_I },
{ XKB_KEY_dead_o, Key_Dead_o },
{ XKB_KEY_dead_O, Key_Dead_O },
{ XKB_KEY_dead_u, Key_Dead_u },
{ XKB_KEY_dead_U, Key_Dead_U },
{ XKB_KEY_dead_small_schwa, Key_Dead_Small_Schwa },
{ XKB_KEY_dead_capital_schwa, Key_Dead_Capital_Schwa },
{ XKB_KEY_dead_greek, Key_Dead_Greek },
{ 0xfe90, Key_Dead_Lowline },
{ 0xfe91, Key_Dead_Aboveverticalline },
{ 0xfe92, Key_Dead_Belowverticalline },
{ 0xfe93, Key_Dead_Longsolidusoverlay },
// Special keys from X.org - This include multimedia keys,
// wireless/bluetooth/uwb keys, special launcher keys, etc.
{ XKB_KEY_XF86Back, Key_Back },
{ XKB_KEY_XF86Forward, Key_Forward },
{ XKB_KEY_XF86Stop, Key_Stop },
{ XKB_KEY_XF86Refresh, Key_Refresh },
{ XKB_KEY_XF86Favorites, Key_Favorites },
{ XKB_KEY_XF86AudioMedia, Key_LaunchMedia },
{ XKB_KEY_XF86OpenURL, Key_OpenUrl },
{ XKB_KEY_XF86HomePage, Key_HomePage },
{ XKB_KEY_XF86Search, Key_Search },
{ XKB_KEY_XF86AudioLowerVolume, Key_VolumeDown },
{ XKB_KEY_XF86AudioMute, Key_VolumeMute },
{ XKB_KEY_XF86AudioRaiseVolume, Key_VolumeUp },
{ XKB_KEY_XF86AudioPlay, Key_MediaPlay },
{ XKB_KEY_XF86AudioStop, Key_MediaStop },
{ XKB_KEY_XF86AudioPrev, Key_MediaPrevious },
{ XKB_KEY_XF86AudioNext, Key_MediaNext },
{ XKB_KEY_XF86AudioRecord, Key_MediaRecord },
{ XKB_KEY_XF86AudioPause, Key_MediaPause },
{ XKB_KEY_XF86Mail, Key_LaunchMail },
{ XKB_KEY_XF86MyComputer, Key_Launch0 }, // ### Qt 6: remap properly
{ XKB_KEY_XF86Calculator, Key_Launch1 },
{ XKB_KEY_XF86Memo, Key_Memo },
{ XKB_KEY_XF86ToDoList, Key_ToDoList },
{ XKB_KEY_XF86Calendar, Key_Calendar },
{ XKB_KEY_XF86PowerDown, Key_PowerDown },
{ XKB_KEY_XF86ContrastAdjust, Key_ContrastAdjust },
{ XKB_KEY_XF86Standby, Key_Standby },
{ XKB_KEY_XF86MonBrightnessUp, Key_MonBrightnessUp },
{ XKB_KEY_XF86MonBrightnessDown, Key_MonBrightnessDown },
{ XKB_KEY_XF86KbdLightOnOff, Key_KeyboardLightOnOff },
{ XKB_KEY_XF86KbdBrightnessUp, Key_KeyboardBrightnessUp },
{ XKB_KEY_XF86KbdBrightnessDown, Key_KeyboardBrightnessDown },
{ XKB_KEY_XF86PowerOff, Key_PowerOff },
{ XKB_KEY_XF86WakeUp, Key_WakeUp },
{ XKB_KEY_XF86Eject, Key_Eject },
{ XKB_KEY_XF86ScreenSaver, Key_ScreenSaver },
{ XKB_KEY_XF86WWW, Key_WWW },
{ XKB_KEY_XF86Sleep, Key_Sleep },
{ XKB_KEY_XF86LightBulb, Key_LightBulb },
{ XKB_KEY_XF86Shop, Key_Shop },
{ XKB_KEY_XF86History, Key_History },
{ XKB_KEY_XF86AddFavorite, Key_AddFavorite },
{ XKB_KEY_XF86HotLinks, Key_HotLinks },
{ XKB_KEY_XF86BrightnessAdjust, Key_BrightnessAdjust },
{ XKB_KEY_XF86Finance, Key_Finance },
{ XKB_KEY_XF86Community, Key_Community },
{ XKB_KEY_XF86AudioRewind, Key_AudioRewind },
{ XKB_KEY_XF86BackForward, Key_BackForward },
{ XKB_KEY_XF86ApplicationLeft, Key_ApplicationLeft },
{ XKB_KEY_XF86ApplicationRight, Key_ApplicationRight },
{ XKB_KEY_XF86Book, Key_Book },
{ XKB_KEY_XF86CD, Key_CD },
{ XKB_KEY_XF86Calculator, Key_Calculator },
{ XKB_KEY_XF86Clear, Key_Clear },
{ XKB_KEY_XF86ClearGrab, Key_ClearGrab },
{ XKB_KEY_XF86Close, Key_Close },
{ XKB_KEY_XF86Copy, Key_Copy },
{ XKB_KEY_XF86Cut, Key_Cut },
{ XKB_KEY_XF86Display, Key_Display },
{ XKB_KEY_XF86DOS, Key_DOS },
{ XKB_KEY_XF86Documents, Key_Documents },
{ XKB_KEY_XF86Excel, Key_Excel },
{ XKB_KEY_XF86Explorer, Key_Explorer },
{ XKB_KEY_XF86Game, Key_Game },
{ XKB_KEY_XF86Go, Key_Go },
{ XKB_KEY_XF86iTouch, Key_iTouch },
{ XKB_KEY_XF86LogOff, Key_LogOff },
{ XKB_KEY_XF86Market, Key_Market },
{ XKB_KEY_XF86Meeting, Key_Meeting },
{ XKB_KEY_XF86MenuKB, Key_MenuKB },
{ XKB_KEY_XF86MenuPB, Key_MenuPB },
{ XKB_KEY_XF86MySites, Key_MySites },
{ XKB_KEY_XF86New, Key_New },
{ XKB_KEY_XF86News, Key_News },
{ XKB_KEY_XF86OfficeHome, Key_OfficeHome },
{ XKB_KEY_XF86Open, Key_Open },
{ XKB_KEY_XF86Option, Key_Option },
{ XKB_KEY_XF86Paste, Key_Paste },
{ XKB_KEY_XF86Phone, Key_Phone },
{ XKB_KEY_XF86Reply, Key_Reply },
{ XKB_KEY_XF86Reload, Key_Reload },
{ XKB_KEY_XF86RotateWindows, Key_RotateWindows },
{ XKB_KEY_XF86RotationPB, Key_RotationPB },
{ XKB_KEY_XF86RotationKB, Key_RotationKB },
{ XKB_KEY_XF86Save, Key_Save },
{ XKB_KEY_XF86Send, Key_Send },
{ XKB_KEY_XF86Spell, Key_Spell },
{ XKB_KEY_XF86SplitScreen, Key_SplitScreen },
{ XKB_KEY_XF86Support, Key_Support },
{ XKB_KEY_XF86TaskPane, Key_TaskPane },
{ XKB_KEY_XF86Terminal, Key_Terminal },
{ XKB_KEY_XF86Tools, Key_Tools },
{ XKB_KEY_XF86Travel, Key_Travel },
{ XKB_KEY_XF86Video, Key_Video },
{ XKB_KEY_XF86Word, Key_Word },
{ XKB_KEY_XF86Xfer, Key_Xfer },
{ XKB_KEY_XF86ZoomIn, Key_ZoomIn },
{ XKB_KEY_XF86ZoomOut, Key_ZoomOut },
{ XKB_KEY_XF86Away, Key_Away },
{ XKB_KEY_XF86Messenger, Key_Messenger },
{ XKB_KEY_XF86WebCam, Key_WebCam },
{ XKB_KEY_XF86MailForward, Key_MailForward },
{ XKB_KEY_XF86Pictures, Key_Pictures },
{ XKB_KEY_XF86Music, Key_Music },
{ XKB_KEY_XF86Battery, Key_Battery },
{ XKB_KEY_XF86WLAN, Key_WLAN },
{ XKB_KEY_XF86UWB, Key_UWB },
{ XKB_KEY_XF86AudioForward, Key_AudioForward },
{ XKB_KEY_XF86AudioRepeat, Key_AudioRepeat },
{ XKB_KEY_XF86AudioRandomPlay, Key_AudioRandomPlay },
{ XKB_KEY_XF86Subtitle, Key_Subtitle },
{ XKB_KEY_XF86AudioCycleTrack, Key_AudioCycleTrack },
{ XKB_KEY_XF86Time, Key_Time },
{ XKB_KEY_XF86Select, Key_Select },
{ XKB_KEY_XF86View, Key_View },
{ XKB_KEY_XF86TopMenu, Key_TopMenu },
{ XKB_KEY_XF86Red, Key_Red },
{ XKB_KEY_XF86Green, Key_Green },
{ XKB_KEY_XF86Yellow, Key_Yellow },
{ XKB_KEY_XF86Blue, Key_Blue },
{ XKB_KEY_XF86Bluetooth, Key_Bluetooth },
{ XKB_KEY_XF86Suspend, Key_Suspend },
{ XKB_KEY_XF86Hibernate, Key_Hibernate },
{ XKB_KEY_XF86TouchpadToggle, Key_TouchpadToggle },
{ XKB_KEY_XF86TouchpadOn, Key_TouchpadOn },
{ XKB_KEY_XF86TouchpadOff, Key_TouchpadOff },
{ XKB_KEY_XF86AudioMicMute, Key_MicMute },
{ XKB_KEY_XF86RFKill, Key_RFKill },
{ XKB_KEY_XF86Launch0, Key_Launch2 }, // ### Qt 6: remap properly
{ XKB_KEY_XF86Launch1, Key_Launch3 },
{ XKB_KEY_XF86Launch2, Key_Launch4 },
{ XKB_KEY_XF86Launch3, Key_Launch5 },
{ XKB_KEY_XF86Launch4, Key_Launch6 },
{ XKB_KEY_XF86Launch5, Key_Launch7 },
{ XKB_KEY_XF86Launch6, Key_Launch8 },
{ XKB_KEY_XF86Launch7, Key_Launch9 },
{ XKB_KEY_XF86Launch8, Key_LaunchA },
{ XKB_KEY_XF86Launch9, Key_LaunchB },
{ XKB_KEY_XF86LaunchA, Key_LaunchC },
{ XKB_KEY_XF86LaunchB, Key_LaunchD },
{ XKB_KEY_XF86LaunchC, Key_LaunchE },
{ XKB_KEY_XF86LaunchD, Key_LaunchF },
{ XKB_KEY_XF86LaunchE, Key_LaunchG },
{ XKB_KEY_XF86LaunchF, Key_LaunchH },
};
static int compare_qtkey(const void *p1, const void *p2)
{
enum QtKey k1 = ((struct Xkb2Qt *)p1)->qtkey;
enum QtKey k2 = ((struct Xkb2Qt *)p2)->qtkey;
// one qtkey may has multiple xkb keysym, fixup it please
if (k1 == k2) {
fprintf(stderr, "// something goes wrong, multiple 0x%08x\n", k1);
}
return k1 > k2 ? 1 : (k1 == k2 ? 0 : -1);
}
static int compare_keysym(const void *p1, const void *p2)
{
int32_t k1 = ((struct Xkb2Qt *)p1)->keysym;
int32_t k2 = ((struct Xkb2Qt *)p2)->keysym;
// one qtkey may has multiple xkb keysym, fixup it please
if (k1 == k2) {
fprintf(stderr, "// something goes wrong, multiple 0x%08x\n", k1);
}
return k1 > k2 ? 1 : (k1 == k2 ? 0 : -1);
}
int main(int argc, char *argv[])
{
size_t count = sizeof(KeyTbl) / sizeof(struct Xkb2Qt);
/* sort the table */
qsort(KeyTbl, count, sizeof(struct Xkb2Qt), compare_qtkey);
/* print the result to file */
fprintf(stdout,
"/* Generated by qtkey-mapper */\n\n"
"#ifndef _QTKEY_MAP_H_\n"
"#define _QTKEY_MAP_H_\n\n"
"#define KEY_MAP_COUNT (%lu)\n\n",
count);
fprintf(stdout, "static const struct qtkey_map {\n"
" unsigned int qtkey;\n"
" unsigned int keysym;\n"
"} qtkey_map_table[] = {\n");
const struct Xkb2Qt *map;
size_t row = count / 3;
size_t left = count % 3;
for (size_t i = 0; i < row; i++) {
map = &KeyTbl[i * 3];
fprintf(stdout, " { 0x%08x, 0x%08x }, { 0x%08x, 0x%08x }, { 0x%08x, 0x%08x },\n",
map->qtkey, map->keysym, (map + 1)->qtkey, (map + 1)->keysym, (map + 2)->qtkey,
(map + 2)->keysym);
}
if (left) {
fprintf(stdout, " ");
}
for (size_t i = 0; i < left; i++) {
map = &KeyTbl[row * 3 + i];
fprintf(stdout, "{ 0x%08x, 0x%08x }, ", map->qtkey, map->keysym);
}
if (left) {
fprintf(stdout, "\n");
}
fprintf(stdout, "};\n\n");
size_t i, j;
struct Xkb2Qt temp;
for (i = 0; i < count - 1; i++) {
for (j = 0; j < count - 1 - i; j++) {
if (KeyTbl[j].keysym > KeyTbl[j + 1].keysym) {
temp = KeyTbl[j];
KeyTbl[j] = KeyTbl[j + 1];
KeyTbl[j + 1] = temp;
}
}
}
/* sort the table */
qsort(KeyTbl, count, sizeof(struct Xkb2Qt), compare_keysym);
fprintf(stdout, "static const struct qtkey_map keysym_map_table[] = {\n");
for (size_t i = 0; i < row; i++) {
map = &KeyTbl[i * 3];
fprintf(stdout, " { 0x%08x, 0x%08x }, { 0x%08x, 0x%08x }, { 0x%08x, 0x%08x },\n",
map->qtkey, map->keysym, (map + 1)->qtkey, (map + 1)->keysym, (map + 2)->qtkey,
(map + 2)->keysym);
}
if (left) {
fprintf(stdout, " ");
}
for (size_t i = 0; i < left; i++) {
map = &KeyTbl[row * 3 + i];
fprintf(stdout, "{ 0x%08x, 0x%08x }, ", map->qtkey, map->keysym);
}
if (left) {
fprintf(stdout, "\n");
}
fprintf(stdout, "};\n\n#endif\n");
return 0;
}
kylin-wayland-compositor/src/config/meson.build 0000664 0001750 0001750 00000001420 15160460057 020656 0 ustar feng feng wlcom_sources += files(
'common.c',
'config.c',
)
if have_kde_global_accel
qtkey_mapper_gen = executable('qtkey-mapper', 'qtkey_mapper.c')
map_table_h = custom_target(
'qtkey_map_table.h',
output : 'qtkey_map_table.h',
capture: true,
command : [qtkey_mapper_gen],
)
wlcom_sources += map_table_h
wlcom_sources += files(
'kde_global_accel.c',
)
endif
if have_kde_input
wlcom_sources += files(
'kde_input.c',
)
endif
if have_ukui_shortcut
wlcom_sources += files(
'ukui_shortcut.c',
)
endif
if have_ukui_gsettings
wlcom_sources += files(
'ukui_gsettings.c',
)
glib = dependency('glib-2.0', version: '>= 2.64')
wlcom_deps += glib
endif
if have_ukui_view_mode
wlcom_sources += files(
'ukui_view_mode.c',
)
endif
kylin-wayland-compositor/src/config/ukui_gsettings.c 0000664 0001750 0001750 00000032646 15160461067 021744 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include "config_p.h"
#include "effect/effect.h"
#include "input/input.h"
#include "server.h"
#include "theme.h"
#include "util/color.h"
#include "util/dbus.h"
#include "util/macros.h"
struct ukui_settings {
struct {
GSettings *settings;
char *theme;
int size;
} cursor;
struct {
GSettings *settings;
char *font_name;
char *font_size;
enum theme_type type;
char *widget_theme;
bool window_radius_is_string;
} style;
struct {
GSettings *settings;
char *picture_file;
int picture_options;
int primary_color;
} background;
struct wl_listener destroy;
};
#define UKUI_THEME_LIGHT "ukui-light"
#define UKUI_THEME_DARK "ukui-dark"
static const char *cursor_path = "/org/ukui/desktop/peripherals/mouse/";
#define CURSOR_PATH_LEN (36)
static const char *style_path = "/org/ukui/style/";
#define STYLE_PATH_LEN (16)
static const char *background_path = "/org/mate/desktop/background/";
#define BACKGROUND_PATH_LEN (29)
static const char *cursor_schema = "org.ukui.peripherals-mouse";
static const char *cursor_theme_key = "cursor-theme";
static const char *cursor_size_key = "cursor-size";
static const char *locate_pointer_key = "locate-pointer";
static const char *shake_cursor_key = "shake-cursor";
static const char *style_schema = "org.ukui.style";
static const char *style_name_key = "style-name";
static const char *icon_theme_key = "icon-theme-name";
static const char *widget_theme_key = "widget-theme-name";
static const char *font_name_key = "system-font";
static const char *font_size_key = "system-font-size";
static const char *accent_color_key = "theme-color";
static const char *window_radius_key = "window-radius";
static const char *menu_transparency_key = "menu-transparency";
static const char *background_schema = "org.mate.background";
static const char *background_picture_key = "picture-filename";
static const char *background_options_key = "picture-options";
static const char *background_color_key = "primary-color";
static const struct ukui_accent_color {
char *name;
int32_t color;
} ukui_accent_colors[] = {
{ "daybreakBlue", 0x3790FA }, { "jamPurple", 0x722ED1 }, { "magenta", 0xEB3096 },
{ "sunRed", 0xF3222D }, { "sunsetOrange", 0xF68C27 }, { "dustGold", 0xF9C53D },
{ "polarGreen", 0x52C429 },
};
static const struct {
char *name;
enum background_option option;
} background_options[] = {
{ "scaled", BACKGROUND_OPTION_SCALED }, { "wallpaper", BACKGROUND_OPTION_WALLPAPER },
{ "centered", BACKGROUND_OPTION_CENTERED }, { "stretched", BACKGROUND_OPTION_STRETCHED },
{ "zoom", BACKGROUND_OPTION_ZOOM }, { "spanned", BACKGROUND_OPTION_SPANNED },
};
static struct ukui_settings *settings = NULL;
static GSettingsSchema *is_schema_installed(const char *schema_id)
{
GSettingsSchemaSource *source = g_settings_schema_source_get_default();
return g_settings_schema_source_lookup(source, schema_id, TRUE);
}
static void handle_cursor_settings_changed(GSettingsSchema *schema, GSettings *mouse,
const char *key)
{
if (strcmp(key, locate_pointer_key) == 0) {
bool enabled = g_settings_get_boolean(mouse, key);
struct effect *effect = effect_by_name("locate_pointer");
if (effect) {
effect_set_enabled(effect, enabled);
}
return;
} else if (strcmp(key, shake_cursor_key) == 0) {
bool enabled = g_settings_get_boolean(mouse, key);
struct effect *effect = effect_by_name("shake_cursor");
if (effect) {
effect_set_enabled(effect, enabled);
}
return;
}
if (strcmp(key, cursor_theme_key) == 0) {
free(settings->cursor.theme);
settings->cursor.theme = g_settings_get_string(mouse, key);
} else if (strcmp(key, cursor_size_key) == 0) {
settings->cursor.size = g_settings_get_int(mouse, key);
} else {
return;
}
if (settings->cursor.theme && settings->cursor.size > 0) {
input_set_all_cursor(settings->cursor.theme, settings->cursor.size);
}
}
static void style_name_changed(GSettings *style, const char *key)
{
if (strcmp(widget_theme_key, key) == 0) {
free(settings->style.widget_theme);
settings->style.widget_theme = g_settings_get_string(style, key);
} else {
const char *style_name = g_settings_get_string(style, key);
if (!strcmp(style_name, UKUI_THEME_LIGHT)) {
settings->style.type = THEME_TYPE_LIGHT;
} else if (!strcmp(style_name, UKUI_THEME_DARK)) {
settings->style.type = THEME_TYPE_DARK;
}
free((void *)style_name);
}
if (settings->style.type != THEME_TYPE_UNDEFINED) {
theme_manager_set_widget_theme(settings->style.widget_theme, settings->style.type);
}
}
static void icon_theme_changed(GSettings *style, const char *key)
{
const char *icon_theme = g_settings_get_string(style, key);
theme_manager_set_icon_theme(icon_theme);
free((void *)icon_theme);
}
static void font_style_changed(GSettings *style, const char *key)
{
if (strcmp(key, font_name_key) == 0) {
free(settings->style.font_name);
settings->style.font_name = g_settings_get_string(style, key);
} else {
free(settings->style.font_size);
settings->style.font_size = g_settings_get_string(style, key);
}
if (settings->style.font_name && settings->style.font_size) {
theme_manager_set_font(settings->style.font_name, round(atof(settings->style.font_size)));
}
}
static void accent_color_changed(GSettings *style, const char *key)
{
const char *accent_color = g_settings_get_string(style, key);
/* try '6,192,199,1' format first */
struct color color;
int num = sscanf(accent_color, "%hhu,%hhu,%hhu,%f", &color.r, &color.g, &color.b, &color.a);
if (num == 4) {
theme_manager_set_accent_color(color_to_uint24(&color));
free((void *)accent_color);
return;
}
/* fallback to old format */
for (size_t i = 0; i < ARRAY_SIZE(ukui_accent_colors); i++) {
if (strcmp(accent_color, ukui_accent_colors[i].name) == 0) {
theme_manager_set_accent_color(ukui_accent_colors[i].color);
break;
}
}
free((void *)accent_color);
}
static void window_radius_changed(GSettings *style, const char *key)
{
int window_radius = -1, menu_radius = -1;
if (settings->style.window_radius_is_string) {
const char *radius = g_settings_get_string(style, key);
int num = sscanf(radius, "%d,%d", &window_radius, &menu_radius);
free((void *)radius);
if (num < 1 || window_radius < 0) {
return;
}
} else {
window_radius = g_settings_get_int(style, key);
}
theme_manager_set_corner_radius(window_radius, menu_radius);
}
static void menu_transparency_changed(GSettings *style, const char *key)
{
int menu_transparency = g_settings_get_int(style, key);
theme_manager_set_opacity(menu_transparency);
}
static void handle_style_settings_changed(GSettingsSchema *schema, GSettings *style,
const char *key)
{
if (strcmp(key, style_name_key) == 0 || (strcmp(key, widget_theme_key) == 0)) {
style_name_changed(style, key);
} else if (strcmp(key, icon_theme_key) == 0) {
icon_theme_changed(style, key);
} else if (strcmp(key, font_name_key) == 0 || strcmp(key, font_size_key) == 0) {
font_style_changed(style, key);
} else if (strcmp(key, accent_color_key) == 0) {
accent_color_changed(style, key);
} else if (strcmp(key, window_radius_key) == 0) {
if (schema) {
GSettingsSchemaKey *schema_key = g_settings_schema_get_key(schema, key);
const GVariantType *value_type = g_settings_schema_key_get_value_type(schema_key);
if (g_variant_type_equal(value_type, G_VARIANT_TYPE_STRING)) {
settings->style.window_radius_is_string = true;
}
g_settings_schema_key_unref(schema_key);
}
window_radius_changed(style, key);
} else if (strcmp(key, menu_transparency_key) == 0) {
menu_transparency_changed(style, key);
}
}
static int options_strings_to_option(const char *options)
{
size_t num = sizeof(background_options) / sizeof(background_options[0]);
for (size_t i = 0; i < num; i++) {
if (strcmp(options, background_options[i].name) == 0) {
return background_options[i].option;
}
}
return BACKGROUND_OPTION_STRETCHED;
}
static void handle_background_settings_changed(GSettingsSchema *schema, GSettings *background,
const char *key)
{
if (strcmp(key, background_picture_key) == 0) {
free(settings->background.picture_file);
settings->background.picture_file = g_settings_get_string(background, key);
} else if (strcmp(key, background_options_key) == 0) {
const char *options = g_settings_get_string(background, key);
settings->background.picture_options = options_strings_to_option(options);
free((void *)options);
} else if (strcmp(key, background_color_key) == 0) {
const char *color = g_settings_get_string(background, key);
sscanf(color + 1, "%x", &settings->background.primary_color);
settings->background.primary_color &= 0xffffff;
free((void *)color);
} else {
return;
}
bool has_picture = settings->background.picture_file && *settings->background.picture_file &&
settings->background.picture_options != 0;
bool has_solid = settings->background.picture_file && !*settings->background.picture_file &&
settings->background.primary_color != 0;
if (has_picture || has_solid) {
theme_manager_set_background(has_picture ? settings->background.picture_file : NULL,
settings->background.picture_options,
settings->background.primary_color);
}
}
static int dconf_notify(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *prefix;
CK(sd_bus_message_read_basic(msg, 's', &prefix));
CK(sd_bus_message_enter_container(msg, 'a', "s"));
const char *change;
char key[128];
int ret = 0;
/* get current changed key */
while (true) {
ret = sd_bus_message_read(msg, "s", &change);
if (ret < 0) {
return ret;
} else if (ret == 0) {
break;
}
snprintf(key, 128, "%s%s", prefix, change);
if (strncmp(key, style_path, STYLE_PATH_LEN) == 0) {
handle_style_settings_changed(NULL, settings->style.settings, key + STYLE_PATH_LEN);
} else if (strncmp(key, cursor_path, CURSOR_PATH_LEN) == 0) {
handle_cursor_settings_changed(NULL, settings->cursor.settings, key + CURSOR_PATH_LEN);
} else if (strncmp(key, background_path, BACKGROUND_PATH_LEN) == 0) {
handle_background_settings_changed(NULL, settings->background.settings,
key + BACKGROUND_PATH_LEN);
}
}
return 0;
}
static GSettings *init_schema_settings(const char *schema_id,
void (*handler)(GSettingsSchema *schema, GSettings *settings,
const char *key))
{
GSettingsSchema *schema = is_schema_installed(schema_id);
if (!schema) {
return NULL;
}
GSettings *settings = g_settings_new(schema_id);
if (!settings) {
g_settings_schema_unref(schema);
return NULL;
}
gchar **keys = g_settings_schema_list_keys(schema);
for (int i = 0; keys && keys[i] != NULL; i++) {
handler(schema, settings, keys[i]);
}
g_strfreev(keys);
g_settings_schema_unref(schema);
return settings;
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&settings->destroy.link);
g_object_unref(settings->cursor.settings);
g_object_unref(settings->style.settings);
g_object_unref(settings->background.settings);
free(settings->cursor.theme);
free(settings->style.font_name);
free(settings->style.font_size);
free(settings->style.widget_theme);
free(settings->background.picture_file);
free(settings);
}
bool ukui_gsettings_create(struct config_manager *config_manager)
{
settings = calloc(1, sizeof(*settings));
if (!settings) {
return false;
}
settings->style.type = THEME_TYPE_UNDEFINED;
settings->cursor.settings = init_schema_settings(cursor_schema, handle_cursor_settings_changed);
settings->style.settings = init_schema_settings(style_schema, handle_style_settings_changed);
if (!settings->cursor.settings && !settings->style.settings) {
free(settings);
settings = NULL;
return false;
}
settings->background.settings =
init_schema_settings(background_schema, handle_background_settings_changed);
/* monitor dconf dbus notify */
dbus_match_signal(NULL, "/ca/desrt/dconf/Writer/user", "ca.desrt.dconf.Writer", "Notify",
dconf_notify, NULL);
settings->destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(config_manager->server->display, &settings->destroy);
return true;
}
kylin-wayland-compositor/src/config/ukui_shortcut.c 0000664 0001750 0001750 00000014411 15160461067 021576 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include "config_p.h"
#include "server.h"
#include "util/dbus.h"
struct shortcut_service {
char *name;
uint32_t black_mask; /* blacklist mask */
uint32_t white_mask; /* whitelist mask */
bool block_all;
struct wl_list link;
};
struct ukui_shortcut_manager {
struct wl_list services;
struct wl_listener destroy;
};
const char *service_path = "/org/ukui/settingsDaemon/shortcut";
const char *service_interface = "org.ukui.settingsDaemon.shortcut";
static struct ukui_shortcut_manager *shortcut_manager = NULL;
static int block_shortcuts_message_handler(sd_bus_message *msg, void *userdata, sd_bus_error *error)
{
struct shortcut_service *service = userdata;
CK(sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, "s"));
const char *type_name;
while (sd_bus_message_read(msg, "s", &type_name) > 0) {
bool found;
enum key_binding_type type = kywc_key_binding_type_by_name(type_name, &found);
if (!found) {
continue;
}
kywc_log(KYWC_INFO, "Block keybinding type: %s", type_name);
/* blocking all */
if (type == KEY_BINDING_TYPE_NUM) {
service->block_all = true;
kywc_key_binding_block_all(true);
break;
}
/* blocking types based on backlist */
kywc_key_binding_block_type(type, true);
service->black_mask |= 1 << type;
}
CK(sd_bus_message_exit_container(msg));
return 0;
}
static int unblock_shortcuts_message_handler(sd_bus_message *msg, void *userdata,
sd_bus_error *error)
{
struct shortcut_service *service = userdata;
CK(sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, "s"));
const char *type_name;
while (sd_bus_message_read(msg, "s", &type_name) > 0) {
bool found;
enum key_binding_type type = kywc_key_binding_type_by_name(type_name, &found);
if (!found) {
continue;
}
service->white_mask |= 1 << type;
}
kywc_log(KYWC_INFO, "Unblock whitelist mask: %x", service->white_mask);
if (service->white_mask) {
for (size_t i = KEY_BINDING_TYPE_CUSTOM_DEF; i < KEY_BINDING_TYPE_NUM; i++) {
/* blocking type is not in the whitelist */
if (!(service->white_mask & (1 << i))) {
kywc_log(KYWC_DEBUG, "Block by binding type index: %ld", i);
kywc_key_binding_block_type(i, true);
continue;
}
}
}
CK(sd_bus_message_exit_container(msg));
return 0;
}
static void shortcut_service_destroy(struct shortcut_service *service)
{
for (size_t i = KEY_BINDING_TYPE_CUSTOM_DEF; i < KEY_BINDING_TYPE_NUM; i++) {
/* recovery of blocked types that are not on the whitelist */
if (service->white_mask && !(service->white_mask & (1 << i))) {
kywc_key_binding_block_type(i, false);
}
/* this type is not in the backlist */
if (!(service->black_mask & (1 << i))) {
continue;
}
/* recovery of blocked types based on backlist */
kywc_key_binding_block_type(i, false);
}
if (service->block_all) {
kywc_key_binding_block_all(false);
}
wl_list_remove(&service->link);
free(service->name);
free(service);
}
static void ukui_shortcut_service_destroy(const char *name)
{
struct shortcut_service *service, *service_tmp;
wl_list_for_each_safe(service, service_tmp, &shortcut_manager->services, link) {
if (strcmp(service->name, name)) {
continue;
}
shortcut_service_destroy(service);
}
}
static void ukui_shortcut_service_create(const char *name)
{
struct shortcut_service *service = calloc(1, sizeof(*service));
if (!service) {
return;
}
if (!dbus_call_method(name, service_path, service_interface, "blockShortcuts",
block_shortcuts_message_handler, service)) {
kywc_log_errno(KYWC_ERROR, "Dbus call service:%s bolckShortcuts method failed", name);
}
if (!dbus_call_method(name, service_path, service_interface, "unblockShortcuts",
unblock_shortcuts_message_handler, service)) {
kywc_log_errno(KYWC_ERROR, "Dbus call service:%s unblockShortcuts method failed", name);
}
service->name = strdup(name);
wl_list_insert(&shortcut_manager->services, &service->link);
}
static int service_message_handler(sd_bus_message *msg, void *userdata, sd_bus_error *error)
{
const char *name = NULL, *old_owner = NULL, *new_owner = NULL;
CK(sd_bus_message_read(msg, "sss", &name, &old_owner, &new_owner));
if (!name || (strncmp(name, "org.ukui.settingsDaemon", 23))) {
return 0;
}
if (old_owner && !*old_owner) {
ukui_shortcut_service_create(name);
kywc_log(KYWC_INFO, "Service: %s registered with owner %s", name, new_owner);
} else if (new_owner && !*new_owner) {
ukui_shortcut_service_destroy(name);
kywc_log(KYWC_INFO, "Service: %s unregistered from owner %s", name, old_owner);
}
return 0;
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&shortcut_manager->destroy.link);
struct shortcut_service *service, *service_tmp;
wl_list_for_each_safe(service, service_tmp, &shortcut_manager->services, link) {
shortcut_service_destroy(service);
}
free(shortcut_manager);
}
bool ukui_shortcut_manager_create(struct config_manager *config_manager)
{
const char *match = "type=signal,interface=org.freedesktop.DBus,member=NameOwnerChanged";
if (!dbus_add_match(match, service_message_handler, NULL)) {
kywc_log(KYWC_ERROR, "Ukui_shortcuts_manager listener dbus service failed");
return false;
}
shortcut_manager = calloc(1, sizeof(*shortcut_manager));
if (!shortcut_manager) {
return false;
}
wl_list_init(&shortcut_manager->services);
shortcut_manager->destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(config_manager->server->display, &shortcut_manager->destroy);
return true;
}
kylin-wayland-compositor/src/config/kde_global_accel.c 0000664 0001750 0001750 00000116177 15160461067 022114 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
// for enum wlr_keyboard_modifier
#include
#include
#include
#include "config_p.h"
#include "qtkey_map_table.h"
#include "server.h"
#include "util/dbus.h"
/**
* convert qtkey to xkb keysym and modifiers
*/
enum Key {
Key_0 = 0x30,
Key_9 = 0x39,
Key_F1 = 0x01000030,
NoModifier = 0x00000000,
ShiftModifier = 0x02000000,
ControlModifier = 0x04000000,
AltModifier = 0x08000000,
MetaModifier = 0x10000000,
KeypadModifier = 0x20000000,
GroupSwitchModifier = 0x40000000,
// Do not extend the mask to include 0x01000000
KeyboardModifierMask = 0xfe000000,
KeyMask = ~KeyboardModifierMask,
};
static int compare_key(const void *p1, const void *p2)
{
int32_t k1 = ((struct qtkey_map *)p1)->qtkey;
int32_t k2 = ((struct qtkey_map *)p2)->qtkey;
return k1 > k2 ? 1 : (k1 == k2 ? 0 : -1);
}
static uint32_t qtkey_to_modifiers(int32_t key)
{
uint32_t modifiers = 0;
if (key & ShiftModifier) {
modifiers |= WLR_MODIFIER_SHIFT;
}
if (key & ControlModifier) {
modifiers |= WLR_MODIFIER_CTRL;
}
if (key & AltModifier) {
modifiers |= WLR_MODIFIER_ALT;
}
if (key & MetaModifier) {
modifiers |= WLR_MODIFIER_LOGO;
}
if (key & KeypadModifier) {
modifiers |= WLR_MODIFIER_MOD2;
}
if (key & GroupSwitchModifier) {
modifiers |= WLR_MODIFIER_MOD5;
}
return modifiers;
}
static uint32_t qtkey_to_keysym(int32_t key)
{
uint32_t sym = XKB_KEY_NoSymbol;
uint32_t qtKey = key & ~KeyboardModifierMask;
if (key & KeypadModifier && qtKey >= Key_0 && qtKey <= Key_9) {
sym = XKB_KEY_KP_0 + qtKey - Key_0;
} else if (qtKey < 0x1000 && !(key & KeypadModifier)) {
sym = tolower(qtKey);
}
/* bsearch the key_map_table */
if (sym == XKB_KEY_NoSymbol) {
struct qtkey_map k = { .qtkey = qtKey };
struct qtkey_map *map =
bsearch(&k, qtkey_map_table, KEY_MAP_COUNT, sizeof(struct qtkey_map), compare_key);
if (map) {
sym = map->keysym;
}
}
if (sym == XKB_KEY_NoSymbol) {
kywc_log(KYWC_WARN, "Cannot covert qtkey 0x%08x to xkb symbol", key);
}
return sym;
}
static int compare_keysym(const void *p1, const void *p2)
{
int32_t k1 = ((struct qtkey_map *)p1)->keysym;
int32_t k2 = ((struct qtkey_map *)p2)->keysym;
return k1 > k2 ? 1 : (k1 == k2 ? 0 : -1);
}
static uint32_t modifiers_to_qtkey(int32_t key)
{
uint32_t qtkey = 0;
if (key & WLR_MODIFIER_SHIFT) {
qtkey |= ShiftModifier;
}
if (key & WLR_MODIFIER_CTRL) {
qtkey |= ControlModifier;
}
if (key & WLR_MODIFIER_ALT) {
qtkey |= AltModifier;
}
if (key & WLR_MODIFIER_LOGO) {
qtkey |= MetaModifier;
}
if (key & WLR_MODIFIER_MOD2) {
qtkey |= KeypadModifier;
}
if (key & WLR_MODIFIER_MOD5) {
qtkey |= GroupSwitchModifier;
}
return qtkey;
}
static uint32_t keysym_to_qtkey(int32_t keysym)
{
uint32_t qtKey = KeyMask;
if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) {
qtKey = keysym + Key_0 - XKB_KEY_KP_0;
} else if (keysym < XKB_KEY_ydiaeresis) {
qtKey = toupper(keysym);
} else if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_R15) {
qtKey = keysym - XKB_KEY_F1 + Key_F1;
}
if (qtKey == KeyMask) {
struct qtkey_map k = { .keysym = keysym };
struct qtkey_map *map =
bsearch(&k, keysym_map_table, KEY_MAP_COUNT, sizeof(struct qtkey_map), compare_keysym);
if (map) {
qtKey = map->qtkey;
}
}
if (qtKey == KeyMask) {
kywc_log(KYWC_WARN, "Cannot covert xkb symbol 0x%08x to qtkey", keysym);
}
return qtKey;
}
/**
* dbus server for kde kglobalaccel
*/
/* Index for actionId QStringLists */
enum actionIdFields {
ComponentUnique = 0, //!< Components Unique Name (ID)
ActionUnique = 1, //!< Actions Unique Name(ID)
ComponentFriendly = 2, //!< Components Friendly Translated Name
ActionFriendly = 3, //!< Actions Friendly Translated Name
};
enum SetShortcutFlag {
SetPresent = 2,
NoAutoloading = 4,
IsDefault = 8,
IsGrab = 16,
};
struct global_shortcut_registry {
struct wl_list components;
struct wl_listener destroy;
};
struct global_shortcut_component {
struct wl_list link;
/* dbus for per component */
const char *dbus_path;
char *unique_name;
char *friendly_name;
struct wl_list contexts;
/* current context in component, "default" "Default Context" */
struct global_shortcut_context *current;
};
struct global_shortcut_context {
struct wl_list link;
struct global_shortcut_component *component;
char *unique_name;
char *friendly_name;
struct wl_list shortcuts;
};
struct global_shortcut {
struct wl_list link;
struct global_shortcut_context *context;
char *unique_name;
char *friendly_name;
struct key_binding *binding;
/* keys. default_keys, mostly combined with modifiers */
int32_t key;
int32_t default_key;
/* means the associated application is present */
bool is_present;
/* means the shortcut is registered with key_binding */
bool is_registered;
/* means the shortcut is new */
bool is_fresh;
/* means if the shorctut is triggered in grab */
bool is_grab;
};
// TODO: add global_shortcut_action to support key list
static const char *registry_bus = "org.kde.kglobalaccel";
static const char *registry_path = "/kglobalaccel";
static const char *registry_interface = "org.kde.KGlobalAccel";
static const char *component_path_prefix = "/component";
static const char *component_interface = "org.kde.kglobalaccel.Component";
struct global_shortcut_registry *registry = NULL;
static struct global_shortcut_component *
global_shortcut_registry_get_component(const char *unique_name)
{
struct global_shortcut_component *component;
wl_list_for_each(component, ®istry->components, link) {
if (strcmp(unique_name, component->unique_name) == 0) {
return component;
}
}
return NULL;
}
static struct global_shortcut *global_shortcut_create(struct global_shortcut_context *context,
const char *unique_name,
const char *friendly_name)
{
struct global_shortcut *shortcut = calloc(1, sizeof(*shortcut));
if (!shortcut) {
return NULL;
}
shortcut->is_fresh = true;
shortcut->is_present = false;
shortcut->is_registered = false;
shortcut->unique_name = strdup(unique_name);
shortcut->friendly_name = strdup(friendly_name);
shortcut->context = context;
wl_list_insert(&context->shortcuts, &shortcut->link);
return shortcut;
}
static void global_shortcut_destroy(struct global_shortcut *shortcut, bool clean)
{
wl_list_remove(&shortcut->link);
free(shortcut->unique_name);
free(shortcut->friendly_name);
/* destroyed in runtime */
if (clean && shortcut->binding) {
kywc_key_binding_destroy(shortcut->binding);
}
free(shortcut);
}
static struct global_shortcut_context *
global_shortcut_context_create(struct global_shortcut_component *component, const char *unique_name,
const char *friendly_name)
{
struct global_shortcut_context *context = calloc(1, sizeof(*context));
if (!context) {
return NULL;
}
wl_list_init(&context->shortcuts);
wl_list_insert(&component->contexts, &context->link);
context->component = component;
context->unique_name = strdup(unique_name);
context->friendly_name = strdup(friendly_name);
return context;
}
static void global_shortcut_context_destroy(struct global_shortcut_context *context)
{
wl_list_remove(&context->link);
free(context->unique_name);
free(context->friendly_name);
struct global_shortcut *shortcut, *tmp;
wl_list_for_each_safe(shortcut, tmp, &context->shortcuts, link) {
global_shortcut_destroy(shortcut, false);
}
free(context);
}
static struct global_shortcut_context *
global_shortcut_component_get_context(struct global_shortcut_component *component,
const char *context_name)
{
struct global_shortcut_context *context;
wl_list_for_each(context, &component->contexts, link) {
if (strcmp(context->unique_name, context_name) == 0) {
return context;
}
}
return NULL;
}
static struct global_shortcut *
global_shortcut_context_get_shortcut_by_key(struct global_shortcut_context *context, int32_t key)
{
struct global_shortcut *shortcut = NULL;
wl_list_for_each(shortcut, &context->shortcuts, link) {
if (shortcut->key == key) {
return shortcut;
}
}
return NULL;
}
static struct global_shortcut *
global_shortcut_context_get_shortcut_by_name(struct global_shortcut_context *context,
const char *shortcut_unique)
{
struct global_shortcut *shortcut = NULL;
wl_list_for_each(shortcut, &context->shortcuts, link) {
if (strcmp(shortcut_unique, shortcut->unique_name) == 0) {
return shortcut;
}
}
return NULL;
}
/* get shortcut in component current context */
static struct global_shortcut *global_shortcut_registry_get_shortcut_by_key(int32_t key)
{
struct global_shortcut *shortcut;
struct global_shortcut_component *component;
wl_list_for_each(component, ®istry->components, link) {
shortcut = global_shortcut_context_get_shortcut_by_key(component->current, key);
if (shortcut) {
return shortcut;
}
}
return NULL;
}
static struct global_shortcut *
global_shortcut_registry_get_shortcut_by_name(const char *component_unique,
const char *shortcut_unique)
{
struct global_shortcut_component *component =
global_shortcut_registry_get_component(component_unique);
if (component) {
struct global_shortcut *shortcut =
global_shortcut_context_get_shortcut_by_name(component->current, shortcut_unique);
if (shortcut) {
return shortcut;
}
}
return NULL;
}
static void global_shortcut_action(struct key_binding *bindbing, void *data)
{
struct global_shortcut *shortcut = data;
dbus_emit_signal(shortcut->context->component->dbus_path, component_interface,
"globalShortcutPressed", "ssx", shortcut->context->component->unique_name,
shortcut->unique_name, 0);
}
static void global_shortcut_create_binding(struct global_shortcut *shortcut)
{
uint32_t keysym = qtkey_to_keysym(shortcut->key);
uint32_t modifiers = qtkey_to_modifiers(shortcut->key);
if (shortcut->binding) {
kywc_key_binding_update(shortcut->binding, keysym, modifiers, NULL);
} else {
shortcut->binding = kywc_key_binding_create_by_symbol(
keysym, modifiers, false, shortcut->is_grab, shortcut->unique_name);
}
}
static void global_shortcut_set_active(struct global_shortcut *shortcut)
{
if (!shortcut->is_present || shortcut->is_registered || !shortcut->binding) {
return;
}
/* register the key binding */
shortcut->is_registered = kywc_key_binding_register(
shortcut->binding, KEY_BINDING_TYPE_CUSTOM_DEF, global_shortcut_action, shortcut);
if (!shortcut->is_registered) {
kywc_key_binding_destroy(shortcut->binding);
shortcut->binding = NULL;
}
}
static void global_shortcut_set_inactive(struct global_shortcut *shortcut)
{
if (!shortcut->is_registered) {
return;
}
kywc_key_binding_unregister(shortcut->binding);
shortcut->is_registered = false;
}
/**
* dbus process functions for org.kde.kglobalaccel.Component
*/
// SD_BUS_PROPERTY("friendlyName", "s", friendly_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
static int friendly_name(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct global_shortcut_component *component = userdata;
CK(sd_bus_message_append_basic(reply, 's', component->friendly_name));
return 0;
}
static int unique_name(sd_bus *bus, const char *path, const char *interface, const char *property,
sd_bus_message *reply, void *userdata, sd_bus_error *ret_error)
{
struct global_shortcut_component *component = userdata;
CK(sd_bus_message_append_basic(reply, 's', component->unique_name));
return 0;
}
// SD_BUS_METHOD("allShortcutInfos", "s", "a(ssssssaiai)", all_shortcut_infos_ex, 0),
static int all_shortcut_infos(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *context_unique = NULL;
CK(sd_bus_message_read(msg, "s", &context_unique));
struct global_shortcut_component *component = userdata;
struct global_shortcut_context *context = global_shortcut_component_get_context(
component, context_unique ? context_unique : "default");
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
CK(sd_bus_message_open_container(reply, 'a', "(ssssssaiai)"));
if (context) {
struct global_shortcut *shortcut;
wl_list_for_each(shortcut, &context->shortcuts, link) {
CK(sd_bus_message_append(reply, "(ssssssaiai)", context->unique_name,
context->friendly_name, component->unique_name,
component->friendly_name, shortcut->unique_name,
shortcut->friendly_name, 4, shortcut->key, 0, 0, 0, 4,
shortcut->default_key, 0, 0, 0));
}
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
// SD_BUS_METHOD("cleanUp", "", "b", clean_up, 0),
static int clean_up(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
struct global_shortcut_component *component = userdata;
bool changed = false;
struct global_shortcut *shortcut;
wl_list_for_each(shortcut, &component->current->shortcuts, link) {
if (!shortcut->is_present) {
changed = true;
global_shortcut_destroy(shortcut, true);
}
}
return sd_bus_reply_method_return(msg, "b", changed);
}
// SD_BUS_METHOD("getShortcutContexts", "", "as", get_shortcut_contexts, 0),
static int get_shortcut_contexts(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
struct global_shortcut_component *component = userdata;
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
CK(sd_bus_message_open_container(reply, 'a', "s"));
struct global_shortcut_context *context;
wl_list_for_each(context, &component->contexts, link) {
CK(sd_bus_message_append(reply, "s", context->unique_name));
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
// SD_BUS_METHOD("invokeShortcut", "ss", "", invoke_shortcut, 0),
static int invoke_shortcut(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *shortcut_unique, *context_unique = NULL;
CK(sd_bus_message_read(msg, "ss", &shortcut_unique, &context_unique));
struct global_shortcut_component *component = userdata;
struct global_shortcut_context *context = global_shortcut_component_get_context(
component, context_unique ? context_unique : "default");
if (context) {
struct global_shortcut *shortcut =
global_shortcut_context_get_shortcut_by_name(context, shortcut_unique);
if (shortcut) {
dbus_emit_signal(shortcut->context->component->dbus_path, component_interface,
"globalShortcutPressed", "ssx",
shortcut->context->component->unique_name, shortcut->unique_name, 0);
}
}
return sd_bus_reply_method_return(msg, NULL);
}
// SD_BUS_METHOD("isActive", "", "b", is_active, 0),
static int is_active(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
struct global_shortcut_component *component = userdata;
bool is_active = false;
// The component is active if at least one of it's global shortcuts is present.
struct global_shortcut *shortcut;
wl_list_for_each(shortcut, &component->current->shortcuts, link) {
if (shortcut->is_present) {
is_active = true;
break;
}
}
return sd_bus_reply_method_return(msg, "b", is_active);
}
// SD_BUS_METHOD("shortcutNames", "s", "as", shortcut_names_ex, 0),
static int shortcut_names(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *context_unique = NULL;
CK(sd_bus_message_read(msg, "s", &context_unique));
struct global_shortcut_component *component = userdata;
struct global_shortcut_context *context = global_shortcut_component_get_context(
component, context_unique ? context_unique : "default");
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
CK(sd_bus_message_open_container(reply, 'a', "s"));
if (context) {
struct global_shortcut *shortcut;
wl_list_for_each(shortcut, &context->shortcuts, link) {
CK(sd_bus_message_append(reply, "s", shortcut->unique_name));
}
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
static const sd_bus_vtable kglobalaccel_component_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_SIGNAL("globalShortcutPressed", "ssx", 0),
SD_BUS_PROPERTY("friendlyName", "s", friendly_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("uniqueName", "s", unique_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD("allShortcutInfos", "s", "a(ssssssaiai)", all_shortcut_infos, 0),
SD_BUS_METHOD("cleanUp", "", "b", clean_up, 0),
SD_BUS_METHOD("getShortcutContexts", "", "as", get_shortcut_contexts, 0),
SD_BUS_METHOD("invokeShortcut", "ss", "", invoke_shortcut, 0),
SD_BUS_METHOD("isActive", "", "b", is_active, 0),
SD_BUS_METHOD("shortcutNames", "s", "as", shortcut_names, 0),
SD_BUS_VTABLE_END,
};
static const char *get_dbus_path(const char *name)
{
int len = snprintf(NULL, 0, "%s/%s", component_path_prefix, name) + 1;
char *path = malloc(len);
if (!path) {
return NULL;
}
snprintf(path, len, "%s/%s", component_path_prefix, name);
/* DBus path can only contain ASCII characters */
char *p = path + strlen(component_path_prefix) + 1;
for (; *p; ++p) {
if (!isalnum(*p) || (*p & ~0x7f) != 0) {
*p = '_';
}
}
return path;
}
static struct global_shortcut_component *global_shortcut_component_create(const char *unique_name,
const char *friendly_name)
{
struct global_shortcut_component *component = calloc(1, sizeof(*component));
if (!component) {
return NULL;
}
wl_list_init(&component->contexts);
wl_list_insert(®istry->components, &component->link);
component->unique_name = strdup(unique_name);
component->friendly_name = strdup(friendly_name);
/* create the default context in the component */
component->current = global_shortcut_context_create(component, "default", "Default Context");
/* register component dbus */
component->dbus_path = get_dbus_path(unique_name);
dbus_register_object(NULL, component->dbus_path, component_interface,
kglobalaccel_component_vtable, component);
return component;
}
static void global_shortcut_component_destroy(struct global_shortcut_component *component)
{
wl_list_remove(&component->link);
free(component->unique_name);
free(component->friendly_name);
free((void *)component->dbus_path);
struct global_shortcut_context *context, *tmp;
wl_list_for_each_safe(context, tmp, &component->contexts, link) {
global_shortcut_context_destroy(context);
}
free(component);
}
/**
* dbus process functions for org.kde.KGlobalAccel
*/
// SD_BUS_METHOD("actionList", "(ai)", "as", action_list, 0),
static int action_list(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
int32_t key;
CK(sd_bus_message_read(msg, "(ai)", 4, &key, NULL, NULL, NULL));
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
struct global_shortcut *shortcut = global_shortcut_registry_get_shortcut_by_key(key);
if (shortcut) {
CK(sd_bus_message_append(reply, "as", 4, shortcut->context->component->unique_name,
shortcut->unique_name, shortcut->context->component->friendly_name,
shortcut->friendly_name));
}
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
// SD_BUS_METHOD("activateGlobalShortcutContext", "ss", "", activate_global_shortcut_context, 0),
static int activate_global_shortcut_context(sd_bus_message *msg, void *userdata,
sd_bus_error *ret_error)
{
const char *component_unique, *context_unique;
CK(sd_bus_message_read(msg, "ss", &component_unique, &context_unique));
struct global_shortcut_component *component =
global_shortcut_registry_get_component(component_unique);
if (component) {
struct global_shortcut_context *context =
global_shortcut_component_get_context(component, context_unique);
if (context) {
struct global_shortcut *shortcut;
wl_list_for_each(shortcut, &component->current->shortcuts, link) {
global_shortcut_set_inactive(shortcut);
}
component->current = context;
}
}
return sd_bus_reply_method_return(msg, NULL);
}
// SD_BUS_METHOD("allActionsForComponent", "as", "aas", all_actions_for_component, 0),
static int all_actions_for_component(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *component_unique;
/* only need component unique name in action_id */
CK(sd_bus_message_read(msg, "as", 4, &component_unique, NULL, NULL, NULL));
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
CK(sd_bus_message_open_container(reply, 'a', "as"));
struct global_shortcut_component *component =
global_shortcut_registry_get_component(component_unique);
if (component) {
struct global_shortcut_context *context =
global_shortcut_component_get_context(component, "default");
struct global_shortcut *shortcut;
wl_list_for_each(shortcut, &context->shortcuts, link) {
if (shortcut->is_fresh) {
continue;
}
CK(sd_bus_message_append(reply, "as", 4, component->unique_name, shortcut->unique_name,
component->friendly_name, shortcut->friendly_name));
}
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
// SD_BUS_METHOD("allComponents", "", "ao", all_components, 0),
static int all_components(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
CK(sd_bus_message_open_container(reply, 'a', "o"));
struct global_shortcut_component *component;
wl_list_for_each(component, ®istry->components, link) {
CK(sd_bus_message_append_basic(reply, 'o', component->dbus_path));
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
// SD_BUS_METHOD("allMainComponents", "", "aas", all_main_components, 0),
static int all_main_components(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
CK(sd_bus_message_open_container(reply, 'a', "as"));
struct global_shortcut_component *component;
wl_list_for_each(component, ®istry->components, link) {
CK(sd_bus_message_append(reply, "as", 4, component->unique_name, "",
component->friendly_name, ""));
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
// SD_BUS_METHOD("blockGlobalShortcuts", "b", "", block_global_shortcuts, 0),
static int block_global_shortcuts(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
uint32_t block;
CK(sd_bus_message_read(msg, "b", &block));
/* activate and deactivate all shortcuts in component current context */
kywc_key_binding_block_all(block);
return sd_bus_reply_method_return(msg, NULL);
}
// SD_BUS_METHOD("defaultShortcutKeys", "as", "a(ai)", default_shortcut_keys, 0),
static int default_shortcut_keys(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *component_unique, *action_unique;
CK(sd_bus_message_read(msg, "as", 4, &component_unique, &action_unique, NULL, NULL));
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
CK(sd_bus_message_open_container(reply, 'a', "(ai)"));
struct global_shortcut *shortcut =
global_shortcut_registry_get_shortcut_by_name(component_unique, action_unique);
if (shortcut) {
CK(sd_bus_message_append(reply, "(ai)", 4, shortcut->default_key, 0, 0, 0));
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
// SD_BUS_METHOD("doRegister", "as", "", do_register, 0),
static int do_register(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *component_unique, *action_unique, *component_friendly, *action_friendly;
CK(sd_bus_message_read(msg, "as", 4, &component_unique, &action_unique, &component_friendly,
&action_friendly));
struct global_shortcut *shortcut =
global_shortcut_registry_get_shortcut_by_name(component_unique, action_unique);
if (shortcut) {
/* replace friendly names */
struct global_shortcut_component *component = shortcut->context->component;
if (*component_friendly && strcmp(component->friendly_name, component_friendly)) {
free(component->friendly_name);
component->friendly_name = strdup(component_friendly);
}
if (*action_friendly && strcmp(shortcut->friendly_name, action_friendly)) {
free(shortcut->friendly_name);
shortcut->friendly_name = strdup(action_friendly);
}
} else {
/* create a shortcut */
struct global_shortcut_component *component =
global_shortcut_registry_get_component(component_unique);
/* Create the component if necessary */
if (!component) {
component = global_shortcut_component_create(component_unique, component_friendly);
}
global_shortcut_create(component->current, action_unique, action_friendly);
}
return sd_bus_reply_method_return(msg, NULL);
}
// SD_BUS_METHOD("getComponent", "s", "o", get_component, 0),
static int get_component(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *component_unique;
CK(sd_bus_message_read(msg, "s", &component_unique));
struct global_shortcut_component *component =
global_shortcut_registry_get_component(component_unique);
if (!component) {
const sd_bus_error error = SD_BUS_ERROR_MAKE_CONST("org.kde.kglobalaccel.NoSuchComponent",
"The component doesn't exist.");
return sd_bus_reply_method_error(msg, &error);
}
return sd_bus_reply_method_return(msg, "o", component->dbus_path);
}
// SD_BUS_METHOD("globalShortcutsByKey", "(ai)i", "a(ssssssaiai)", global_shortcuts_by_key, 0),
static int global_shortcuts_by_key(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
int32_t key, type;
CK(sd_bus_message_read(msg, "(ai)i", 4, &key, NULL, NULL, NULL, &type));
struct global_shortcut_component *component;
struct global_shortcut_context *context;
struct global_shortcut *shortcut;
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
CK(sd_bus_message_open_container(reply, 'a', "(ssssssaiai)"));
wl_list_for_each(component, ®istry->components, link) {
wl_list_for_each(context, &component->contexts, link) {
shortcut = global_shortcut_context_get_shortcut_by_key(context, key);
if (shortcut) {
CK(sd_bus_message_append(reply, "(ssssssaiai)", context->unique_name,
context->friendly_name, component->unique_name,
component->friendly_name, shortcut->unique_name,
shortcut->friendly_name, 4, shortcut->key, 0, 0, 0, 4,
shortcut->default_key, 0, 0, 0));
}
}
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
// SD_BUS_METHOD("globalShortcutAvailable", "(ai)s", "b", global_shortcut_available, 0),
static int global_shortcut_available(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *component_unique;
int32_t key;
CK(sd_bus_message_read(msg, "(ai)s", 4, &key, NULL, NULL, NULL, &component_unique));
struct global_shortcut *shortcut = NULL;
struct global_shortcut_component *component =
global_shortcut_registry_get_component(component_unique);
if (component) {
struct global_shortcut_context *context;
wl_list_for_each(context, &component->contexts, link) {
shortcut = global_shortcut_context_get_shortcut_by_key(context, key);
if (shortcut) {
break;
}
}
}
return sd_bus_reply_method_return(msg, "b", !!shortcut);
}
// SD_BUS_METHOD("setForeignShortcutKeys", "asa(ai)", "", set_foreign_shortcut_keys, 0),
static int set_foreign_shortcut_keys(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *component_unique, *action_unique;
int32_t key;
CK(sd_bus_message_read(msg, "as", 4, &component_unique, &action_unique, NULL, NULL));
CK(sd_bus_message_enter_container(msg, 'a', "(ai)"));
CK(sd_bus_message_read(msg, "(ai)", 4, &key, NULL, NULL, NULL));
// TODO: check key list: sd_bus_message_read(msg, "(ai)", 4, NULL, NULL, NULL, NULL) == 0
CK(sd_bus_message_exit_container(msg));
struct global_shortcut *shortcut =
global_shortcut_registry_get_shortcut_by_name(component_unique, action_unique);
if (shortcut) {
shortcut->key = key;
dbus_emit_signal(registry_path, registry_interface, "yourShortcutsChanged", "asa(ai)", 4,
shortcut->context->component->unique_name, shortcut->unique_name,
shortcut->context->component->friendly_name, shortcut->friendly_name, 1, 4,
shortcut->key, 0, 0, 0);
}
return sd_bus_reply_method_return(msg, NULL);
}
// SD_BUS_METHOD("setInactive", "as", "", set_inactive, 0),
static int set_inactive(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *component_unique, *action_unique;
CK(sd_bus_message_read(msg, "as", 4, &component_unique, &action_unique, NULL, NULL));
struct global_shortcut *shortcut =
global_shortcut_registry_get_shortcut_by_name(component_unique, action_unique);
if (shortcut) {
shortcut->is_present = false;
global_shortcut_set_inactive(shortcut);
}
return sd_bus_reply_method_return(msg, NULL);
}
// SD_BUS_METHOD("setShortcutKeys", "asa(ai)u", "a(ai)", set_shortcut_keys, 0),
static int set_shortcut_keys(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *component_unique, *action_unique;
int32_t key;
uint32_t flags;
CK(sd_bus_message_read(msg, "as", 4, &component_unique, &action_unique, NULL, NULL));
CK(sd_bus_message_enter_container(msg, 'a', "(ai)"));
CK(sd_bus_message_read(msg, "(ai)", 4, &key, NULL, NULL, NULL));
// TODO: check key list: sd_bus_message_read(msg, "(ai)", 4, NULL, NULL, NULL, NULL) == 0
CK(sd_bus_message_exit_container(msg));
CK(sd_bus_message_read(msg, "u", &flags));
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
CK(sd_bus_message_open_container(reply, 'a', "(ai)"));
struct global_shortcut *shortcut =
global_shortcut_registry_get_shortcut_by_name(component_unique, action_unique);
if (shortcut) {
bool setPresent = (flags & SetPresent);
bool isAutoloading = !(flags & NoAutoloading);
bool isDefault = (flags & IsDefault);
bool isGrab = (flags & IsGrab);
shortcut->is_grab = isGrab;
// default shortcuts cannot clash because they don't do anything
if (isDefault) {
shortcut->default_key = key;
CK(sd_bus_message_append(reply, "(ai)", 4, key, 0, 0, 0));
goto out;
}
if (isAutoloading && !shortcut->is_fresh) {
if (!shortcut->is_present && setPresent) {
shortcut->is_present = true;
global_shortcut_set_active(shortcut);
}
// We are finished here. Return the list of current active keys.
CK(sd_bus_message_append(reply, "(ai)", 4, shortcut->key, 0, 0, 0));
goto out;
}
// now we are actually changing the shortcut of the action
shortcut->key = key;
global_shortcut_create_binding(shortcut);
if (setPresent) {
shortcut->is_present = true;
global_shortcut_set_active(shortcut);
}
shortcut->is_fresh = false;
CK(sd_bus_message_append(reply, "(ai)", 4, shortcut->key, 0, 0, 0));
}
out:
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
// SD_BUS_METHOD("shortcutKeys", "as", "a(ai)", shortcut_keys, 0),
static int shortcut_keys(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *component_unique, *action_unique;
CK(sd_bus_message_read(msg, "as", 4, &component_unique, &action_unique, NULL, NULL));
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(msg, &reply));
CK(sd_bus_message_open_container(reply, 'a', "(ai)"));
struct global_shortcut *shortcut =
global_shortcut_registry_get_shortcut_by_name(component_unique, action_unique);
if (shortcut) {
CK(sd_bus_message_append(reply, "(ai)", 4, shortcut->key, 0, 0, 0));
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
// SD_BUS_METHOD("unregister", "ss", "b", un_register, 0),
static int un_register(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
const char *component_unique, *shortcut_unique;
CK(sd_bus_message_read(msg, "ss", &component_unique, &shortcut_unique));
struct global_shortcut *shortcut =
global_shortcut_registry_get_shortcut_by_name(component_unique, shortcut_unique);
if (shortcut) {
global_shortcut_destroy(shortcut, true);
}
return sd_bus_reply_method_return(msg, "b", !!shortcut);
}
/* only support kglobalaccel >= 5.90 */
static const sd_bus_vtable kglobalaccel_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_SIGNAL("yourShortcutsChanged", "asa(ai)", 0),
SD_BUS_METHOD("actionList", "(ai)", "as", action_list, 0),
SD_BUS_METHOD("activateGlobalShortcutContext", "ss", "", activate_global_shortcut_context, 0),
SD_BUS_METHOD("allActionsForComponent", "as", "aas", all_actions_for_component, 0),
SD_BUS_METHOD("allComponents", "", "ao", all_components, 0),
SD_BUS_METHOD("allMainComponents", "", "aas", all_main_components, 0),
SD_BUS_METHOD("blockGlobalShortcuts", "b", "", block_global_shortcuts, 0),
SD_BUS_METHOD("defaultShortcutKeys", "as", "a(ai)", default_shortcut_keys, 0),
SD_BUS_METHOD("doRegister", "as", "", do_register, 0),
SD_BUS_METHOD("getComponent", "s", "o", get_component, 0),
SD_BUS_METHOD("globalShortcutAvailable", "(ai)s", "b", global_shortcut_available, 0),
SD_BUS_METHOD("globalShortcutsByKey", "(ai)i", "a(ssssssaiai)", global_shortcuts_by_key, 0),
SD_BUS_METHOD("setForeignShortcutKeys", "asa(ai)", "", set_foreign_shortcut_keys, 0),
SD_BUS_METHOD("setInactive", "as", "", set_inactive, 0),
SD_BUS_METHOD("setShortcutKeys", "asa(ai)u", "a(ai)", set_shortcut_keys, 0),
SD_BUS_METHOD("shortcutKeys", "as", "a(ai)", shortcut_keys, 0),
SD_BUS_METHOD("unregister", "ss", "b", un_register, 0),
SD_BUS_VTABLE_END,
};
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(®istry->destroy.link);
/* free all components contexts shortcuts */
struct global_shortcut_component *component, *tmp;
wl_list_for_each_safe(component, tmp, ®istry->components, link) {
global_shortcut_component_destroy(component);
}
free(registry);
registry = NULL;
}
static const char *component_builtin = "kylin-wlcom";
static bool kglobalaccel_builtin_shortcuts(struct key_binding *binding, char *unique_name,
char *friendly_name, int32_t modifiers, int32_t key)
{
struct global_shortcut *shortcut =
global_shortcut_registry_get_shortcut_by_name(component_builtin, unique_name);
if (!shortcut) {
/* create a shortcut */
struct global_shortcut_component *component =
global_shortcut_registry_get_component(component_builtin);
/* Create the component if necessary */
if (!component) {
component = global_shortcut_component_create(component_builtin, friendly_name);
}
shortcut = global_shortcut_create(component->current, unique_name, friendly_name);
}
shortcut->binding = binding;
shortcut->key = modifiers_to_qtkey(modifiers) | keysym_to_qtkey(key);
shortcut->is_present = true;
shortcut->is_registered = true;
return false;
}
bool kde_global_accel_manager_create(struct config_manager *config_manager)
{
registry = calloc(1, sizeof(*registry));
if (!registry) {
return false;
}
if (!dbus_register_object(registry_bus, registry_path, registry_interface, kglobalaccel_vtable,
registry)) {
free(registry);
registry = NULL;
return false;
}
wl_list_init(®istry->components);
kywc_key_binding_for_each(kglobalaccel_builtin_shortcuts);
registry->destroy.notify = handle_server_destroy;
server_add_destroy_listener(config_manager->server, ®istry->destroy);
return true;
}
kylin-wayland-compositor/src/config/ukui_view_mode.c 0000664 0001750 0001750 00000002550 15160460057 021700 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include "config_p.h"
#include "util/dbus.h"
#include "view/view.h"
static int handle_mode_change_signal(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
int tablet_mode = 0;
int ret = sd_bus_message_read(m, "b", &tablet_mode);
if (ret < 0) {
kywc_log(KYWC_WARN, "Failed to parse D-Bus response for mode change: %s", strerror(-ret));
return 0;
}
const char *name = tablet_mode ? "tablet_mode" : "stack_mode";
view_manager_set_view_mode(name);
return 0;
}
bool ukui_view_mode_manager_create(struct config_manager *config_manager)
{
if (!dbus_match_signal("com.kylin.statusmanager.interface", "/",
"com.kylin.statusmanager.interface", "mode_change_signal",
handle_mode_change_signal, NULL)) {
kywc_log(KYWC_ERROR, "Ukui_view_mode_manager match mode_change_signal error");
return false;
}
if (!dbus_call_method("com.kylin.statusmanager.interface", "/",
"com.kylin.statusmanager.interface", "get_current_tabletmode",
handle_mode_change_signal, NULL)) {
kywc_log(KYWC_ERROR, "Ukui_view_mode_manager get current mode error");
return false;
}
return true;
}
kylin-wayland-compositor/src/config/common.c 0000664 0001750 0001750 00000006021 15160461067 020154 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include "config_p.h"
#include "server.h"
#include "util/dbus.h"
#include "util/logger.h"
static const char *service_path = "/com/kylin/Wlcom";
static const char *service_interface = "com.kylin.Wlcom";
static struct {
struct wl_listener destroy;
struct wl_event_source *timer;
} config = { 0 };
static int set_log_level(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
uint32_t level = 0;
CK(sd_bus_message_read(m, "u", &level));
if (level < KYWC_LOG_LEVEL_LAST) {
logger_set_level(level);
return sd_bus_reply_method_return(m, NULL);
}
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid args, please input [0-5].");
return sd_bus_reply_method_error(m, &error);
}
static int set_log_rate_limit(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
uint32_t interval, burst;
CK(sd_bus_message_read(m, "uu", &interval, &burst));
logger_set_rate_limit(interval, burst);
return sd_bus_reply_method_return(m, NULL);
}
static int print_config(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
struct config_manager *cm = userdata;
const char *config = json_object_to_json_string(cm->json);
return sd_bus_reply_method_return(m, "s", config);
}
static int handle_timeout(void *data)
{
wl_event_source_timer_update(config.timer, 5000);
malloc_trim(4096);
return 0;
}
static int trim_memory(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
uint32_t enabled = 0;
CK(sd_bus_message_read(m, "b", &enabled));
if (!!enabled == !!config.timer) {
return sd_bus_reply_method_return(m, NULL);
}
struct config_manager *cm = userdata;
if (enabled) {
config.timer = wl_event_loop_add_timer(cm->server->event_loop, handle_timeout, cm);
wl_event_source_timer_update(config.timer, 5000);
} else {
wl_event_source_remove(config.timer);
config.timer = NULL;
}
return sd_bus_reply_method_return(m, NULL);
}
static const sd_bus_vtable service_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("SetLogLevel", "u", "", set_log_level, 0),
SD_BUS_METHOD("SetLogRateLimit", "uu", "", set_log_rate_limit, 0),
SD_BUS_METHOD("PrintConfig", "", "s", print_config, 0),
SD_BUS_METHOD("TrimMemory", "b", "", trim_memory, 0),
SD_BUS_VTABLE_END,
};
static void handle_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&config.destroy.link);
if (config.timer) {
wl_event_source_remove(config.timer);
}
}
bool config_manager_common_init(struct config_manager *config_manager)
{
if (!dbus_register_object(NULL, service_path, service_interface, service_vtable,
config_manager)) {
return false;
}
config.destroy.notify = handle_destroy;
wl_display_add_destroy_listener(config_manager->server->display, &config.destroy);
return true;
}
kylin-wayland-compositor/src/config/config.c 0000664 0001750 0001750 00000010537 15160461067 020140 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include "config_p.h"
#include "input/input.h"
#include "server.h"
#include "util/file.h"
#include "util/string.h"
static struct config_manager *config_manager = NULL;
static const char *check_config_file(void)
{
char *config_dir = string_expand_path("~/.config/kylin-wlcom");
if (!config_dir) {
return NULL;
}
/* now check config dir */
if (!file_exists(config_dir)) {
kywc_log(KYWC_INFO, "Configure dir %s not exist, create it", config_dir);
int ret = mkdir(config_dir, S_IRWXU | S_IRWXG);
if (ret) {
kywc_log_errno(KYWC_ERROR, "Create configure dir failed");
free(config_dir);
return NULL;
}
}
const char *fullpath = string_join_path(config_dir, NULL, "config.json");
free(config_dir);
return fullpath;
}
void config_manager_sync(void)
{
if (!config_manager->file || !config_manager->json) {
return;
}
json_object_to_file_ext(config_manager->file, config_manager->json,
JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&config_manager->server_destroy.link);
wl_list_remove(&config_manager->server_ready.link);
struct config *config, *config_tmp;
wl_list_for_each_safe(config, config_tmp, &config_manager->configs, link) {
config_destroy(config);
}
config_manager_sync();
json_object_put(config_manager->json);
json_object_put(config_manager->sys_json);
free((void *)config_manager->file);
free(config_manager);
config_manager = NULL;
}
static void handle_server_ready(struct wl_listener *listener, void *data)
{
input_action_manager_create(config_manager->server);
kde_global_accel_manager_create(config_manager);
kde_input_manager_create(config_manager);
ukui_gsettings_create(config_manager);
ukui_shortcut_manager_create(config_manager);
ukui_view_mode_manager_create(config_manager);
}
struct config_manager *config_manager_create(struct server *server)
{
config_manager = calloc(1, sizeof(*config_manager));
if (!config_manager) {
return NULL;
}
/* read config file */
config_manager->file = check_config_file();
if (config_manager->file) {
config_manager->json = json_object_from_file(config_manager->file);
}
/* get system default config */
config_manager->sys_json = json_object_from_file("/etc/kylin-wlcom/config.json");
kywc_log(KYWC_INFO, "Get the sys default config from the etc directory");
if (!config_manager->sys_json) {
kywc_log(KYWC_WARN, "The default config does not exist");
}
/* create one if empty */
if (!config_manager->json) {
config_manager->json = json_object_new_object();
}
config_manager->server = server;
wl_list_init(&config_manager->configs);
config_manager->server_ready.notify = handle_server_ready;
wl_signal_add(&server->events.ready, &config_manager->server_ready);
config_manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &config_manager->server_destroy);
config_manager_common_init(config_manager);
return config_manager;
}
struct config *config_manager_add_config(const char *name)
{
struct config *config = calloc(1, sizeof(*config));
if (!config) {
return NULL;
}
json_object *object = config_manager->json;
json_object *sys_object = config_manager->sys_json;
if (name) {
object = json_object_object_get(config_manager->json, name);
if (!object) {
object = json_object_new_object();
json_object_object_add(config_manager->json, name, object);
}
sys_object = json_object_object_get(config_manager->sys_json, name);
}
config->json = object;
config->sys_json = sys_object;
wl_signal_init(&config->events.destroy);
wl_list_insert(&config_manager->configs, &config->link);
return config;
}
void config_destroy(struct config *config)
{
wl_signal_emit_mutable(&config->events.destroy, NULL);
assert(wl_list_empty(&config->events.destroy.listener_list));
wl_list_remove(&config->link);
free(config);
}
kylin-wayland-compositor/src/input/ 0000775 0001750 0001750 00000000000 15160461067 016413 5 ustar feng feng kylin-wayland-compositor/src/input/meson.build 0000664 0001750 0001750 00000000720 15160460057 020552 0 ustar feng feng wlcom_sources += files(
'action.c',
'binding.c',
'config.c',
'cursor.c',
'event.c',
'gesture.c',
'idle.c',
'idle_inhibit.c',
'input.c',
'text_input.c',
'keyboard.c',
'keyboard_group.c',
'libinput.c',
'monitor.c',
'tablet.c',
'text_input_v1.c',
'text_input_v2.c',
'toplevel_drag.c',
'touch.c',
'transient_seat.c',
'seat.c',
'selection.c',
)
if have_kde_keystate
wlcom_sources += files(
'kde_keystate.c',
)
endif
kylin-wayland-compositor/src/input/input.c 0000664 0001750 0001750 00000061637 15160461067 017733 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "backend/eis.h"
#include "backend/libinput.h"
#include "input/input.h"
#include "input/seat.h"
#include "input_p.h"
#include "output.h"
#include "server.h"
static struct input_manager *input_manager = NULL;
void input_set_all_cursor(const char *cursor_theme, uint32_t cursor_size)
{
struct seat *seat;
wl_list_for_each(seat, &input_manager->seats, link) {
seat_set_cursor(seat, cursor_theme, cursor_size);
}
}
void input_add_new_listener(struct wl_listener *listener)
{
wl_signal_add(&input_manager->events.new_input, listener);
}
static void handle_touchpad_raw_tap(struct wl_listener *listener, void *data)
{
struct input *input = wl_container_of(listener, input, raw_tap);
struct pointer_raw_tap_event *raw_tap_event = data;
kywc_log(KYWC_DEBUG, "handle raw_tap fingers: %d", raw_tap_event->fingers);
gesture_state_begin(input->tap_gestures, GESTURE_TYPE_TAP, GESTURE_DEVICE_TOUCHPAD,
GESTURE_EDGE_NONE, raw_tap_event->fingers);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
assert(wl_list_empty(&input_manager->events.new_input.listener_list));
assert(wl_list_empty(&input_manager->events.new_seat.listener_list));
wl_list_remove(&input_manager->server_destroy.link);
struct seat *seat, *seat_tmp;
wl_list_for_each_safe(seat, seat_tmp, &input_manager->seats, link) {
seat_destroy(seat);
}
struct input_keymap *keymap, *keymap_tmp;
wl_list_for_each_safe(keymap, keymap_tmp, &input_manager->keymaps, link) {
wl_list_remove(&keymap->link);
xkb_keymap_unref(keymap->keymap);
free(keymap);
}
queue_fence_finish(&input_manager->fence);
free(input_manager);
input_manager = NULL;
}
static void handle_primary_output(struct wl_listener *listener, void *data)
{
struct kywc_output *primary = data;
if (!primary) {
return;
}
struct input *input = wl_container_of(listener, input, primary_output);
wl_list_remove(&input->primary_output.link);
wl_list_init(&input->primary_output.link);
struct input_state state = input->state;
state.mapped_to_output = primary->name;
input_set_state(input, &state);
}
static void handle_mapped_output_disable(struct wl_listener *listener, void *data)
{
struct input *input = wl_container_of(listener, input, mapped_output_disable);
/* if output backend is destroyed before input backend */
if (input_manager->server->terminate) {
wl_list_remove(&input->mapped_output_disable.link);
wl_list_init(&input->mapped_output_disable.link);
wl_list_remove(&input->viewport.link);
wl_list_init(&input->viewport.link);
return;
}
/* current mapped output is being off or destroyed */
struct input_state state = input->state;
state.mapped_to_output = NULL;
if (input->prop.type == WLR_INPUT_DEVICE_TOUCH) {
struct kywc_output *primary = kywc_output_get_primary();
/* if it is primary, map to NULL and listen for primary change to remap */
/* otherwise, map to the primary */
if (!primary || primary->destroying) {
kywc_output_add_primary_listener(&input->primary_output);
} else {
state.mapped_to_output = primary->name;
}
}
input_set_state(input, &state);
}
static void handle_mapped_output_viewport(struct wl_listener *listener, void *data)
{
struct input *input = wl_container_of(listener, input, viewport);
struct wlr_cursor *wlr_cursor = input->seat->cursor->wlr_cursor;
struct output *output = output_from_kywc_output(input->mapped_output);
if (!wlr_box_empty(&output->scene_output->viewport.src)) {
wlr_cursor_map_input_to_region(wlr_cursor, input->wlr_input,
&output->scene_output->viewport.src);
} else {
wlr_cursor_map_input_to_output(wlr_cursor, input->wlr_input, output->wlr_output);
}
}
static void input_destroy(struct input *input)
{
/* Tell the user the name of the removed device. */
input_notify_destroy(input);
wl_signal_emit_mutable(&input->events.destroy, NULL);
assert(wl_list_empty(&input->events.destroy.listener_list));
wl_list_remove(&input->link);
wl_list_remove(&input->mapped_output_disable.link);
wl_list_remove(&input->primary_output.link);
wl_list_remove(&input->viewport.link);
wl_list_remove(&input->raw_tap.link);
kywc_log(KYWC_DEBUG, "Input device %s destroy", input->name);
if (input->seat) {
seat_remove_input(input);
}
if (input->tap_gestures) {
gesture_state_finish(input->tap_gestures);
free(input->tap_gestures);
}
free((void *)input->name);
free(input);
}
static void handle_input_destroy(struct wl_listener *listener, void *data)
{
struct input *input = wl_container_of(listener, input, destroy);
wl_list_remove(&input->destroy.link);
input_destroy(input);
}
static void input_get_prop(struct input *input, struct input_prop *prop)
{
struct wlr_input_device *wlr_input = input->wlr_input;
input->prop.type = wlr_input->type;
input->prop.is_virtual = input->name && strncmp(input->name, "V_", 2) == 0;
input->prop.support_mapped_to_output = wlr_input->type == WLR_INPUT_DEVICE_POINTER ||
wlr_input->type == WLR_INPUT_DEVICE_TOUCH ||
wlr_input->type == WLR_INPUT_DEVICE_TABLET;
if (input->device) {
libinput_get_prop(input, prop);
}
}
static void input_get_state(struct input *input, struct input_state *state)
{
struct wlr_input_device *wlr_input = input->wlr_input;
state->seat = input->seat ? input->seat->name : NULL;
state->mapped_to_output = input->mapped_output ? input->mapped_output->name : NULL;
state->scroll_factor = input->state.scroll_factor > 0 ? input->state.scroll_factor
: input->default_state.scroll_factor;
state->double_click_time = input->state.double_click_time > 0
? input->state.double_click_time
: input->default_state.double_click_time;
if (input->prop.type == WLR_INPUT_DEVICE_KEYBOARD) {
struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(wlr_input);
state->repeat_rate = wlr_keyboard->repeat_info.rate;
state->repeat_delay = wlr_keyboard->repeat_info.delay;
}
if (input->device) {
libinput_get_state(input, state);
}
}
static void input_get_default_state(struct input *input, struct input_state *state)
{
state->seat = NULL;
state->mapped_to_output = NULL;
state->scroll_factor = 1.0;
state->double_click_time = DEFAULT_DOUBLE_CLICK_TIME;
if (input->prop.type == WLR_INPUT_DEVICE_KEYBOARD) {
state->repeat_rate = 25;
state->repeat_delay = 600;
}
if (input->device) {
libinput_get_default_state(input, state);
}
}
struct input *input_create(struct wlr_input_device *wlr_input, bool virtual)
{
struct input *input = calloc(1, sizeof(*input));
if (!input) {
return NULL;
}
input->wlr_input = wlr_input;
wlr_input->data = input;
input->destroy.notify = handle_input_destroy;
wl_signal_add(&wlr_input->events.destroy, &input->destroy);
input->manager = input_manager;
wl_signal_init(&input->events.destroy);
wl_list_insert(&input_manager->inputs, &input->link);
input->mapped_output_disable.notify = handle_mapped_output_disable;
input->primary_output.notify = handle_primary_output;
input->viewport.notify = handle_mapped_output_viewport;
input->raw_tap.notify = handle_touchpad_raw_tap;
wl_list_init(&input->mapped_output_disable.link);
wl_list_init(&input->primary_output.link);
wl_list_init(&input->viewport.link);
wl_list_init(&input->raw_tap.link);
if (wlr_input_device_is_libinput(wlr_input)) {
input->device = wlr_libinput_get_device_handle(wlr_input);
}
if (virtual) {
input->name = kywc_identifier_generate("V_%s", wlr_input->name);
}
input_get_prop(input, &input->prop);
if (!input->prop.is_virtual) {
input->name = kywc_identifier_generate("%d:%d:%d:%s", wlr_input->type, input->prop.vendor,
input->prop.product, wlr_input->name);
}
input_get_default_state(input, &input->default_state);
input_get_state(input, &input->state);
// touchpad checked
if (input->prop.type == WLR_INPUT_DEVICE_POINTER && input->prop.tap_finger_count &&
input->device) {
input->tap_gestures = calloc(1, sizeof(*input->tap_gestures));
gesture_state_init(input->tap_gestures, input_manager->server->display);
wlr_libinput_add_raw_tap_listener(input->wlr_input, &input->raw_tap);
}
struct input_state state = input->state;
bool found = input_read_config(input, &state);
if (!found) {
// keep default
}
// map touch screen to primary output by default
if (input->prop.type == WLR_INPUT_DEVICE_TOUCH) {
struct kywc_output *primary = kywc_output_get_primary();
state.mapped_to_output = primary ? primary->name : NULL;
}
input_set_state(input, &state);
wl_signal_emit_mutable(&input_manager->events.new_input, input);
if (kywc_log_get_level() == KYWC_DEBUG) {
kywc_log(KYWC_DEBUG, "Input device %s create", input->name);
input_prop_and_state_debug(input);
}
/* Tell the user the name of the added device. */
input_notify_create(input);
return input;
}
static void handle_new_input(struct wl_listener *listener, void *data)
{
struct wlr_input_device *wlr_input = data;
input_create(wlr_input, false);
}
static void handle_new_virtual_pointer(struct wl_listener *listener, void *data)
{
struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
struct wlr_input_device *wlr_input = &pointer->pointer.base;
struct input *input = input_create(wlr_input, true);
if (!input) {
return;
}
/* apply suggested seat and output */
if (event->suggested_seat || event->suggested_output) {
struct input_state state = input->state;
if (event->suggested_seat) {
state.seat = event->suggested_seat->name;
}
if (event->suggested_output) {
state.mapped_to_output = event->suggested_output->name;
}
input_set_state(input, &state);
}
}
static void handle_new_virtual_keyboard(struct wl_listener *listener, void *data)
{
struct wlr_virtual_keyboard_v1 *keyboard = data;
struct wlr_input_device *wlr_input = &keyboard->keyboard.base;
struct input *input = input_create(wlr_input, true);
if (!input) {
return;
}
/* apply keyboard seat */
if (strcmp(input->seat->name, keyboard->seat->name)) {
struct input_state state = input->state;
state.seat = keyboard->seat->name;
input_set_state(input, &state);
}
}
uint32_t input_manager_for_each_seat(seat_iterator_func_t iterator, void *data)
{
uint32_t index = 0;
struct seat *seat;
wl_list_for_each(seat, &input_manager->seats, link) {
if (iterator(seat, index++, data)) {
break;
}
}
return index;
}
static void handle_keyboard_shortcuts_inhibitor_destroy(struct wl_listener *listener, void *data)
{
struct seat_keyboard_shortcuts_inhibitor *shortcuts_inhibitor =
wl_container_of(listener, shortcuts_inhibitor, destroy);
wl_list_remove(&shortcuts_inhibitor->link);
wl_list_remove(&shortcuts_inhibitor->destroy.link);
free(shortcuts_inhibitor);
}
static void handle_new_shortcuts_inhibitor(struct wl_listener *listener, void *data)
{
struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = data;
struct seat_keyboard_shortcuts_inhibitor *shortcuts_inhibitor =
calloc(1, sizeof(*shortcuts_inhibitor));
if (!shortcuts_inhibitor) {
return;
}
shortcuts_inhibitor->inhibitor = inhibitor;
shortcuts_inhibitor->destroy.notify = handle_keyboard_shortcuts_inhibitor_destroy;
wl_signal_add(&inhibitor->events.destroy, &shortcuts_inhibitor->destroy);
struct seat *seat = seat_from_wlr_seat(inhibitor->seat);
wl_list_insert(&seat->keyboard_shortcuts_inhibitors, &shortcuts_inhibitor->link);
wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor);
}
static void handle_new_pointer_constraint(struct wl_listener *listener, void *data)
{
struct wlr_pointer_constraint_v1 *constraint = data;
struct seat *seat = seat_from_wlr_seat(constraint->seat);
cursor_constraint_create(seat->cursor, constraint);
}
static void handle_request_set_cursor_shape(struct wl_listener *listener, void *data)
{
const struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
struct seat *seat = seat_from_wlr_seat(event->seat_client->seat);
struct wlr_seat_client *focused_client = seat->wlr_seat->pointer_state.focused_client;
if (seat->pointer_grab || focused_client != event->seat_client) {
return;
}
cursor_set_image(seat->cursor, (enum cursor_name)event->shape);
}
struct xkb_keymap *input_get_or_create_keymap(struct keymap_rules *rules, bool wait)
{
if (wait) {
queue_fence_wait(&input_manager->fence);
}
struct input_keymap *keymap;
wl_list_for_each(keymap, &input_manager->keymaps, link) {
if (!keyboard_check_keymap_rules(&keymap->rules, rules)) {
return keymap->keymap;
}
}
keymap = calloc(1, sizeof(*keymap));
if (!keymap) {
return NULL;
}
keymap->keymap = keyboard_compile_keymap(&keymap->rules);
if (!keymap->keymap) {
kywc_log(KYWC_ERROR, "Keymap compile failed, text input is broken");
free(keymap);
return NULL;
}
keymap->rules = *rules;
wl_list_insert(&input_manager->keymaps, &keymap->link);
return keymap->keymap;
}
static void compile_keymap(void *job, void *gdata, int index)
{
struct keymap_rules rules = { 0 };
input_get_or_create_keymap(&rules, false);
}
static void handle_backend_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&input_manager->backend_destroy.link);
wl_list_remove(&input_manager->new_input.link);
wl_list_remove(&input_manager->new_virtual_keyboard.link);
wl_list_remove(&input_manager->new_virtual_pointer.link);
wl_list_remove(&input_manager->new_shortcuts_inhibit.link);
wl_list_remove(&input_manager->new_pointer_constraint.link);
wl_list_remove(&input_manager->request_set_cursor_shape.link);
}
struct input_manager *input_manager_create(struct server *server)
{
input_manager = calloc(1, sizeof(*input_manager));
if (!input_manager) {
return NULL;
}
input_manager->server = server;
wl_list_init(&input_manager->seats);
wl_list_init(&input_manager->inputs);
wl_signal_init(&input_manager->events.new_input);
wl_signal_init(&input_manager->events.new_seat);
input_manager->backend_destroy.notify = handle_backend_destroy;
wl_signal_add(&server->backend->events.destroy, &input_manager->backend_destroy);
input_manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &input_manager->server_destroy);
wl_list_init(&input_manager->keymaps);
queue_fence_init(&input_manager->fence);
if (!queue_add_job(server->queue, input_manager, &input_manager->fence, compile_keymap, NULL)) {
compile_keymap(input_manager, server, -1);
}
input_manager->new_input.notify = handle_new_input;
wl_signal_add(&server->backend->events.new_input, &input_manager->new_input);
struct wlr_backend *eis = eis_backend_create(server->display, server->layout);
if (eis) {
wlr_multi_backend_add(server->backend, eis);
}
input_manager->virtual_pointer = wlr_virtual_pointer_manager_v1_create(server->display);
input_manager->new_virtual_pointer.notify = handle_new_virtual_pointer;
wl_signal_add(&input_manager->virtual_pointer->events.new_virtual_pointer,
&input_manager->new_virtual_pointer);
input_manager->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create(server->display);
input_manager->new_virtual_keyboard.notify = handle_new_virtual_keyboard;
wl_signal_add(&input_manager->virtual_keyboard->events.new_virtual_keyboard,
&input_manager->new_virtual_keyboard);
input_manager->pointer_gestures = wlr_pointer_gestures_v1_create(server->display);
input_manager->relative_pointer = wlr_relative_pointer_manager_v1_create(server->display);
input_manager->shortcuts_inhibit = wlr_keyboard_shortcuts_inhibit_v1_create(server->display);
input_manager->new_shortcuts_inhibit.notify = handle_new_shortcuts_inhibitor;
wl_signal_add(&input_manager->shortcuts_inhibit->events.new_inhibitor,
&input_manager->new_shortcuts_inhibit);
input_manager->pointer_constraints = wlr_pointer_constraints_v1_create(server->display);
input_manager->new_pointer_constraint.notify = handle_new_pointer_constraint;
wl_signal_add(&input_manager->pointer_constraints->events.new_constraint,
&input_manager->new_pointer_constraint);
input_manager->cursor_shape = wlr_cursor_shape_manager_v1_create(server->display, 1);
input_manager->request_set_cursor_shape.notify = handle_request_set_cursor_shape;
wl_signal_add(&input_manager->cursor_shape->events.request_set_shape,
&input_manager->request_set_cursor_shape);
input_manager_config_init(input_manager);
selection_manager_create(input_manager);
input_monitor_create(input_manager);
bindings_create(input_manager);
input_method_manager_create(input_manager);
touch_manager_create(input_manager);
tablet_manager_create(input_manager);
kde_keystate_manager_create(input_manager);
transient_seat_manager_create(input_manager);
idle_manager_create(server);
idle_inhibit_manager_create(server);
/* create the default seat */
input_manager->default_seat = seat_create(input_manager, "seat0");
input_manager->bind_seat = input_set_seat;
return input_manager;
}
void input_set_seat(struct input *input, const char *seat)
{
/* already have attached to seat */
if (input->seat) {
if (!strcmp(seat, input->seat->name)) {
return;
} else {
/* remove from prev seat */
seat_remove_input(input);
}
}
input->seat = seat_by_name(seat);
/* create a new seat */
if (!input->seat) {
input->seat = seat_create(input_manager, seat);
}
seat_add_input(input->seat, input);
}
static bool _input_set_state(struct input *input, struct input_state *state)
{
struct wlr_input_device *wlr_input = input->wlr_input;
/* config keyboard with input state */
if (input->prop.type == WLR_INPUT_DEVICE_KEYBOARD) {
struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(wlr_input);
bool keymap_changed = !wlr_keyboard->keymap ||
keyboard_check_keymap_rules(&input->state.rules, &state->rules);
bool repeat_info_changed = wlr_keyboard->repeat_info.rate != state->repeat_rate ||
wlr_keyboard->repeat_info.delay != state->repeat_delay;
/* we need remove this input and add later */
if (!input->prop.is_virtual && wlr_keyboard->group &&
(keymap_changed || repeat_info_changed)) {
kywc_log(KYWC_DEBUG, "Input %s is removed and be added later", input->name);
seat_remove_input(input);
}
if (keymap_changed) {
struct xkb_keymap *keymap = input_get_or_create_keymap(&state->rules, true);
wlr_keyboard_set_keymap(wlr_keyboard, keymap);
}
if (repeat_info_changed) {
wlr_keyboard_set_repeat_info(wlr_keyboard, state->repeat_rate, state->repeat_delay);
}
}
input->state.scroll_factor = state->scroll_factor;
input->state.double_click_time = state->double_click_time;
/* choose a suitable seat, add the input device to the seat */
input_manager->bind_seat(input, state->seat ? state->seat : "seat0");
if (input->prop.support_mapped_to_output) {
struct kywc_output *mapped_output = NULL;
if (state->mapped_to_output) {
mapped_output = kywc_output_by_name(state->mapped_to_output);
if (mapped_output && (!mapped_output->state.enabled || mapped_output->destroying)) {
kywc_log(KYWC_WARN, "mapped output %s is not available", mapped_output->name);
mapped_output = NULL;
}
}
struct wlr_cursor *wlr_cursor = input->seat->cursor->wlr_cursor;
struct wlr_output *wlr_output =
mapped_output ? output_from_kywc_output(mapped_output)->wlr_output : NULL;
wlr_cursor_map_input_to_output(wlr_cursor, wlr_input, wlr_output);
input->mapped_output = mapped_output;
}
if (input->device) {
return libinput_set_state(input, state);
}
return true;
}
bool input_set_state(struct input *input, struct input_state *state)
{
struct kywc_output *old_mapped_output = input->mapped_output;
bool success = _input_set_state(input, state);
/* update state anyway */
input_get_state(input, &input->state);
if (old_mapped_output != input->mapped_output) {
wl_list_remove(&input->mapped_output_disable.link);
wl_list_init(&input->mapped_output_disable.link);
wl_list_remove(&input->viewport.link);
wl_list_init(&input->viewport.link);
if (input->mapped_output) {
struct output *output = output_from_kywc_output(input->mapped_output);
struct ky_scene_output *scene_output = output->scene_output;
wl_signal_add(&scene_output->events.destroy, &input->mapped_output_disable);
wl_signal_add(&scene_output->events.viewport, &input->viewport);
handle_mapped_output_viewport(&input->viewport, NULL);
}
}
if (!input->prop.is_virtual) {
input_write_config(input);
}
return success;
}
struct input *input_by_name(const char *name)
{
struct input *input;
wl_list_for_each(input, &input_manager->inputs, link) {
if (!strcmp(input->name, name)) {
return input;
}
}
return NULL;
}
struct input *input_from_wlr_input(struct wlr_input_device *wlr_input)
{
return wlr_input->data;
}
struct seat *input_manager_get_default_seat(void)
{
return input_manager->default_seat;
}
void input_manager_set_event_filter(event_filter_func_t filter, void *data)
{
input_manager->event_filter = filter;
input_manager->event_filter_data = data;
}
bool input_event_filter(struct seat *seat, struct input *input, enum input_event_type type,
void *event)
{
idle_manager_notify_activity(seat);
if (!input_manager->event_filter) {
return false;
}
struct input_event ev = {
.seat = seat,
.input = input,
.type = type,
.event = event,
};
return input_manager->event_filter(&ev, input_manager->event_filter_data);
}
struct output *input_current_output(struct seat *seat)
{
struct wlr_output *wlr_output =
wlr_output_layout_output_at(seat->layout, seat->cursor->lx, seat->cursor->ly);
if (!wlr_output) {
/* sync the position in wlr_cursor */
cursor_move(seat->cursor, NULL, 0, 0, true, false);
wlr_output = wlr_output_layout_output_at(seat->layout, seat->cursor->lx, seat->cursor->ly);
}
return wlr_output ? output_from_wlr_output(wlr_output) : NULL;
}
void input_manager_switch_vt(unsigned vt)
{
struct server *server = input_manager->server;
if (server->vtnr == vt) {
return;
}
struct wlr_session *session = server->session;
if (session) {
server->active = false;
if (!wlr_session_change_vt(session, vt)) {
server->active = true;
}
}
}
struct seat *seat_by_name(const char *seat_name)
{
struct seat *seat;
wl_list_for_each(seat, &input_manager->seats, link) {
if (strcmp(seat->name, seat_name) == 0) {
return seat;
}
}
return NULL;
}
void seat_add_new_listener(struct wl_listener *listener)
{
wl_signal_add(&input_manager->events.new_seat, listener);
}
kylin-wayland-compositor/src/input/text_input_v1.c 0000664 0001750 0001750 00000027760 15160461067 021404 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include "text-input-unstable-v1-protocol.h"
#include "text_input_v1.h"
static void text_input_destroy(struct text_input_v1 *text_input)
{
wl_signal_emit_mutable(&text_input->events.destroy, NULL);
assert(wl_list_empty(&text_input->events.activate.listener_list));
assert(wl_list_empty(&text_input->events.deactivate.listener_list));
assert(wl_list_empty(&text_input->events.commit.listener_list));
assert(wl_list_empty(&text_input->events.destroy.listener_list));
wl_resource_set_user_data(text_input->resource, NULL);
wl_list_remove(&text_input->surface_destroy.link);
wl_list_remove(&text_input->seat_destroy.link);
wl_list_remove(&text_input->link);
free(text_input->surrounding.text);
free(text_input);
}
static void text_input_activate(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *seat, struct wl_resource *surface)
{
struct text_input_v1 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat);
if (!seat_client) {
return;
}
text_input->seat = seat_client->seat;
wl_signal_add(&seat_client->events.destroy, &text_input->seat_destroy);
text_input->surface = wlr_surface_from_resource(surface);
wl_signal_add(&text_input->surface->events.destroy, &text_input->surface_destroy);
text_input->activated = true;
wl_signal_emit_mutable(&text_input->events.activate, NULL);
}
static void text_input_deactivate(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *seat)
{
struct text_input_v1 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat);
if (!seat_client) {
return;
}
if (text_input->seat != seat_client->seat) {
return;
}
text_input->activated = false;
wl_signal_emit_mutable(&text_input->events.deactivate, NULL);
text_input->surface = NULL;
text_input->seat = NULL;
wl_list_remove(&text_input->surface_destroy.link);
wl_list_init(&text_input->surface_destroy.link);
wl_list_remove(&text_input->seat_destroy.link);
wl_list_init(&text_input->seat_destroy.link);
}
static void text_input_show_input_panel(struct wl_client *client, struct wl_resource *resource)
{
// Not implemented yet
}
static void text_input_hide_input_panel(struct wl_client *client, struct wl_resource *resource)
{
// Not implemented yet
}
static void text_input_reset(struct wl_client *client, struct wl_resource *resource)
{
struct text_input_v1 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
text_input->surrounding.pending = false;
text_input->content_type.pending = false;
}
static void text_input_set_surrounding_text(struct wl_client *client, struct wl_resource *resource,
const char *text, uint32_t cursor, uint32_t anchor)
{
struct text_input_v1 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
free(text_input->surrounding.text);
text_input->surrounding.text = strdup(text);
if (!text_input->surrounding.text) {
wl_client_post_no_memory(client);
}
text_input->surrounding.cursor = cursor;
text_input->surrounding.anchor = anchor;
text_input->surrounding.pending = true;
}
static void text_input_set_content_type(struct wl_client *client, struct wl_resource *resource,
uint32_t hint, uint32_t purpose)
{
struct text_input_v1 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
/* convert text_input_v1 to text_input_v3 */
text_input->content_type.hint =
hint == ZWP_TEXT_INPUT_V1_CONTENT_HINT_DEFAULT ? ZWP_TEXT_INPUT_V1_CONTENT_HINT_NONE : hint;
text_input->content_type.purpose =
purpose > ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD ? purpose + 1 : purpose;
text_input->content_type.pending = true;
}
static void text_input_set_cursor_rectangle(struct wl_client *client, struct wl_resource *resource,
int32_t x, int32_t y, int32_t width, int32_t height)
{
struct text_input_v1 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
text_input->cursor_rectangle.x = x;
text_input->cursor_rectangle.y = y;
text_input->cursor_rectangle.width = width;
text_input->cursor_rectangle.height = height;
}
static void text_input_set_preferred_language(struct wl_client *client,
struct wl_resource *resource, const char *language)
{
// Not implemented yet
}
static void text_input_commit_state(struct wl_client *client, struct wl_resource *resource,
uint32_t serial)
{
struct text_input_v1 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
if (text_input->surface == NULL) {
kywc_log(KYWC_DEBUG, "Text input commit when surface destroyed");
}
text_input->serial = serial;
wl_signal_emit_mutable(&text_input->events.commit, NULL);
}
static void text_input_invoke_action(struct wl_client *client, struct wl_resource *resource,
uint32_t button, uint32_t index)
{
// Not implemented yet
}
static const struct zwp_text_input_v1_interface text_input_impl = {
.activate = text_input_activate,
.deactivate = text_input_deactivate,
.show_input_panel = text_input_show_input_panel,
.hide_input_panel = text_input_hide_input_panel,
.reset = text_input_reset,
.set_surrounding_text = text_input_set_surrounding_text,
.set_content_type = text_input_set_content_type,
.set_cursor_rectangle = text_input_set_cursor_rectangle,
.set_preferred_language = text_input_set_preferred_language,
.commit_state = text_input_commit_state,
.invoke_action = text_input_invoke_action,
};
static void text_input_resource_destroy(struct wl_resource *resource)
{
struct text_input_v1 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
text_input_destroy(text_input);
}
static void text_input_handle_surface_destroy(struct wl_listener *listener, void *data)
{
struct text_input_v1 *text_input = wl_container_of(listener, text_input, surface_destroy);
text_input_destroy(text_input);
}
static void text_input_handle_seat_destroy(struct wl_listener *listener, void *data)
{
struct text_input_v1 *text_input = wl_container_of(listener, text_input, seat_destroy);
text_input_destroy(text_input);
}
static void text_input_manager_create_text_input(struct wl_client *client,
struct wl_resource *resource, uint32_t id)
{
int version = wl_resource_get_version(resource);
struct wl_resource *text_input_resource =
wl_resource_create(client, &zwp_text_input_v1_interface, version, id);
if (text_input_resource == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(text_input_resource, &text_input_impl, NULL,
text_input_resource_destroy);
struct text_input_v1 *text_input = calloc(1, sizeof(*text_input));
if (!text_input) {
wl_client_post_no_memory(client);
return;
}
wl_signal_init(&text_input->events.activate);
wl_signal_init(&text_input->events.deactivate);
wl_signal_init(&text_input->events.commit);
wl_signal_init(&text_input->events.destroy);
text_input->resource = text_input_resource;
wl_resource_set_user_data(text_input_resource, text_input);
text_input->seat_destroy.notify = text_input_handle_seat_destroy;
wl_list_init(&text_input->seat_destroy.link);
text_input->surface_destroy.notify = text_input_handle_surface_destroy;
wl_list_init(&text_input->surface_destroy.link);
struct text_input_manager_v1 *manager = wl_resource_get_user_data(resource);
wl_list_insert(&manager->text_inputs, &text_input->link);
wl_signal_emit_mutable(&manager->events.text_input, text_input);
}
static const struct zwp_text_input_manager_v1_interface text_input_manager_impl = {
.create_text_input = text_input_manager_create_text_input,
};
static void text_input_manager_bind(struct wl_client *wl_client, void *data, uint32_t version,
uint32_t id)
{
struct text_input_manager_v1 *manager = data;
assert(wl_client && manager);
struct wl_resource *resource =
wl_resource_create(wl_client, &zwp_text_input_manager_v1_interface, version, id);
if (resource == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
wl_resource_set_implementation(resource, &text_input_manager_impl, manager, NULL);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
struct text_input_manager_v1 *manager = wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, manager);
assert(&manager->events.destroy.listener_list);
assert(&manager->events.text_input.listener_list);
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
struct text_input_manager_v1 *text_input_manager_v1_create(struct wl_display *display)
{
struct text_input_manager_v1 *manager = calloc(1, sizeof(*manager));
if (!manager) {
return NULL;
}
wl_list_init(&manager->text_inputs);
wl_signal_init(&manager->events.text_input);
wl_signal_init(&manager->events.destroy);
manager->global = wl_global_create(display, &zwp_text_input_manager_v1_interface, 1, manager,
text_input_manager_bind);
if (!manager->global) {
free(manager);
return NULL;
}
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->display_destroy);
return manager;
}
void text_input_v1_send_enter(struct text_input_v1 *text_input, struct wlr_surface *surface)
{
assert(surface == text_input->surface);
zwp_text_input_v1_send_enter(text_input->resource, text_input->surface->resource);
}
void text_input_v1_send_leave(struct text_input_v1 *text_input)
{
zwp_text_input_v1_send_leave(text_input->resource);
text_input->surface = NULL;
wl_list_remove(&text_input->surface_destroy.link);
wl_list_init(&text_input->surface_destroy.link);
}
void text_input_v1_send_preedit_string(struct text_input_v1 *text_input, const char *text,
int32_t cursor_begin)
{
zwp_text_input_v1_send_preedit_cursor(text_input->resource, cursor_begin);
zwp_text_input_v1_send_preedit_styling(text_input->resource, 0, text ? strlen(text) : 0,
ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_DEFAULT);
zwp_text_input_v1_send_preedit_string(text_input->resource, text_input->serial,
text ? text : "", "");
}
void text_input_v1_send_commit_string(struct text_input_v1 *text_input, const char *text)
{
zwp_text_input_v1_send_commit_string(text_input->resource, text_input->serial, text);
}
void text_input_v1_send_delete_surrounding_text(struct text_input_v1 *text_input, const char *text,
uint32_t before_length, uint32_t after_length)
{
zwp_text_input_v1_send_delete_surrounding_text(
text_input->resource, strlen(text) - before_length, after_length + before_length);
text_input_v1_send_commit_string(text_input, text);
}
kylin-wayland-compositor/src/input/idle_inhibit.c 0000664 0001750 0001750 00000014071 15160460057 021203 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include "input_p.h"
#include "server.h"
#include "view/view.h"
struct idle_inhibit_manager {
struct wlr_idle_inhibit_manager_v1 *idle_inhibit;
struct wl_list idle_inhibitors;
struct wl_listener new_idle_inhibitor;
struct wl_listener destroy;
};
struct idle_inhibitor {
struct wl_list link;
struct idle_inhibit_manager *manager;
struct wl_listener inhibitor_destroy;
struct wlr_surface *surface;
struct wl_listener surface_map;
struct wl_listener surface_unmap;
struct view *view;
struct wl_listener view_map;
struct wl_listener view_minimize;
bool visible;
};
static void idle_inhibitor_set_inhibit(struct idle_inhibitor *idle_inhibitor)
{
if (idle_inhibitor->visible) {
idle_manager_set_inhibited(true);
return;
}
struct idle_inhibit_manager *manager = idle_inhibitor->manager;
struct idle_inhibitor *inhibitor;
wl_list_for_each(inhibitor, &manager->idle_inhibitors, link) {
if (inhibitor->visible) {
idle_manager_set_inhibited(true);
return;
}
}
idle_manager_set_inhibited(false);
}
static void handle_view_map(struct wl_listener *listener, void *data)
{
struct idle_inhibitor *idle_inhibitor = wl_container_of(listener, idle_inhibitor, view_map);
idle_inhibitor->visible = !idle_inhibitor->view->base.minimized;
idle_inhibitor_set_inhibit(idle_inhibitor);
}
static void handle_view_minimize(struct wl_listener *listener, void *data)
{
struct idle_inhibitor *idle_inhibitor =
wl_container_of(listener, idle_inhibitor, view_minimize);
idle_inhibitor->visible = !idle_inhibitor->view->base.minimized;
idle_inhibitor_set_inhibit(idle_inhibitor);
}
static void handle_surface_map(struct wl_listener *listener, void *data)
{
struct idle_inhibitor *idle_inhibitor = wl_container_of(listener, idle_inhibitor, surface_map);
struct view *view = view_try_from_wlr_surface(idle_inhibitor->surface);
if (!view) {
wl_list_init(&idle_inhibitor->view_map.link);
wl_list_init(&idle_inhibitor->view_minimize.link);
idle_inhibitor->visible = true;
idle_inhibitor_set_inhibit(idle_inhibitor);
return;
}
idle_inhibitor->view = view;
wl_signal_add(&view->base.events.map, &idle_inhibitor->view_map);
wl_signal_add(&view->base.events.minimize, &idle_inhibitor->view_minimize);
if (view->base.mapped) {
handle_view_map(&idle_inhibitor->view_map, NULL);
}
}
static void handle_surface_unmap(struct wl_listener *listener, void *data)
{
struct idle_inhibitor *idle_inhibitor =
wl_container_of(listener, idle_inhibitor, surface_unmap);
wl_list_remove(&idle_inhibitor->view_map.link);
wl_list_init(&idle_inhibitor->view_map.link);
wl_list_remove(&idle_inhibitor->view_minimize.link);
wl_list_init(&idle_inhibitor->view_minimize.link);
idle_inhibitor->visible = false;
idle_inhibitor_set_inhibit(idle_inhibitor);
}
static void handle_inhibitor_destroy(struct wl_listener *listener, void *data)
{
struct idle_inhibitor *idle_inhibitor =
wl_container_of(listener, idle_inhibitor, inhibitor_destroy);
wl_list_remove(&idle_inhibitor->link);
wl_list_remove(&idle_inhibitor->inhibitor_destroy.link);
wl_list_remove(&idle_inhibitor->surface_map.link);
wl_list_remove(&idle_inhibitor->surface_unmap.link);
wl_list_remove(&idle_inhibitor->view_map.link);
wl_list_remove(&idle_inhibitor->view_minimize.link);
if (idle_inhibitor->visible) {
idle_inhibitor->visible = false;
idle_inhibitor_set_inhibit(idle_inhibitor);
}
free(idle_inhibitor);
}
static void handle_new_idle_inhibitor(struct wl_listener *listener, void *data)
{
struct idle_inhibit_manager *manager = wl_container_of(listener, manager, new_idle_inhibitor);
struct wlr_idle_inhibitor_v1 *wlr_idle_inhibitor = data;
struct idle_inhibitor *idle_inhibitor = calloc(1, sizeof(*idle_inhibitor));
if (!idle_inhibitor) {
return;
}
idle_inhibitor->manager = manager;
wl_list_insert(&manager->idle_inhibitors, &idle_inhibitor->link);
idle_inhibitor->inhibitor_destroy.notify = handle_inhibitor_destroy;
wl_signal_add(&wlr_idle_inhibitor->events.destroy, &idle_inhibitor->inhibitor_destroy);
idle_inhibitor->surface = wlr_idle_inhibitor->surface;
idle_inhibitor->surface_map.notify = handle_surface_map;
wl_signal_add(&idle_inhibitor->surface->events.map, &idle_inhibitor->surface_map);
idle_inhibitor->surface_unmap.notify = handle_surface_unmap;
wl_signal_add(&idle_inhibitor->surface->events.unmap, &idle_inhibitor->surface_unmap);
idle_inhibitor->view_map.notify = handle_view_map;
wl_list_init(&idle_inhibitor->view_map.link);
idle_inhibitor->view_minimize.notify = handle_view_minimize;
wl_list_init(&idle_inhibitor->view_minimize.link);
if (idle_inhibitor->surface->mapped) {
handle_surface_map(&idle_inhibitor->surface_map, NULL);
}
}
static void handle_destroy(struct wl_listener *listener, void *data)
{
struct idle_inhibit_manager *manager = wl_container_of(listener, manager, destroy);
wl_list_remove(&manager->destroy.link);
wl_list_remove(&manager->new_idle_inhibitor.link);
free(manager);
}
bool idle_inhibit_manager_create(struct server *server)
{
struct idle_inhibit_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->idle_inhibit = wlr_idle_inhibit_v1_create(server->display);
if (!manager->idle_inhibit) {
free(manager);
return false;
}
wl_list_init(&manager->idle_inhibitors);
manager->new_idle_inhibitor.notify = handle_new_idle_inhibitor;
wl_signal_add(&manager->idle_inhibit->events.new_inhibitor, &manager->new_idle_inhibitor);
manager->destroy.notify = handle_destroy;
wl_signal_add(&manager->idle_inhibit->events.destroy, &manager->destroy);
return true;
}
kylin-wayland-compositor/src/input/input_p.h 0000664 0001750 0001750 00000016736 15160461067 020257 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#ifndef _INPUT_P_H_
#define _INPUT_P_H_
#include "input/cursor.h"
#include "input/gesture.h"
#include "input/keyboard.h"
#include "input/seat.h"
#include "util/queue.h"
enum input_lock_key {
INPUT_KEY_CAPSLOCK,
INPUT_KEY_NUMLOCK,
INPUT_KEY_SCROLLLOCK,
};
struct input_keymap {
struct wl_list link;
struct xkb_keymap *keymap;
struct keymap_rules rules;
};
struct input_manager {
struct server *server;
struct wl_list seats;
struct wl_list inputs;
struct wl_list keymaps;
struct queue_fence fence;
struct {
struct wl_signal new_input;
struct wl_signal new_seat;
} events;
struct seat *default_seat;
void (*bind_seat)(struct input *input, const char *seat);
event_filter_func_t event_filter;
void *event_filter_data;
struct config *config;
struct config *seat_config;
struct wl_listener new_input;
struct wl_listener backend_destroy;
struct wl_listener server_destroy;
struct wlr_pointer_gestures_v1 *pointer_gestures;
struct wlr_relative_pointer_manager_v1 *relative_pointer;
struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard;
struct wl_listener new_virtual_keyboard;
struct wlr_virtual_pointer_manager_v1 *virtual_pointer;
struct wl_listener new_virtual_pointer;
struct wlr_keyboard_shortcuts_inhibit_manager_v1 *shortcuts_inhibit;
struct wl_listener new_shortcuts_inhibit;
struct wlr_pointer_constraints_v1 *pointer_constraints;
struct wl_listener new_pointer_constraint;
struct wlr_cursor_shape_manager_v1 *cursor_shape;
struct wl_listener request_set_cursor_shape;
};
bool input_manager_config_init(struct input_manager *input_manager);
bool input_read_config(struct input *input, struct input_state *state);
void input_write_config(struct input *input);
void input_prop_and_state_debug(struct input *input);
void input_manager_switch_vt(unsigned vt);
void input_notify_destroy(struct input *input);
void input_notify_create(struct input *input);
bool input_event_filter(struct seat *seat, struct input *input, enum input_event_type type,
void *event);
struct xkb_keymap *input_get_or_create_keymap(struct keymap_rules *rules, bool wait);
struct seat *seat_by_name(const char *seat_name);
void cursor_set_xcursor_manager(struct cursor *cursor, const char *theme, uint32_t size,
bool saved);
void cursor_set_surface(struct cursor *cursor, struct wlr_surface *surface, int32_t hotspot_x,
int32_t hotspot_y, struct wl_client *client);
bool seat_read_config(struct seat *seat);
void seat_write_config(struct seat *seat);
void seat_feed_pointer_motion(struct seat *seat, double x, double y, bool absolute);
void seat_feed_pointer_button(struct seat *seat, uint32_t button, bool pressed);
void seat_feed_pointer_axis(struct seat *seat, uint32_t axis, double step);
void seat_feed_keyboard_key(struct seat *seat, uint32_t key, bool pressed);
/**
* libinput helper functions
*/
void libinput_get_prop(struct input *input, struct input_prop *prop);
void libinput_get_state(struct input *input, struct input_state *state);
void libinput_get_default_state(struct input *input, struct input_state *state);
bool libinput_set_state(struct input *input, struct input_state *state);
/**
* monitor for input cursor and others
*/
struct input_monitor *input_monitor_create(struct input_manager *input_manager);
/**
* idle manager
*/
bool idle_manager_create(struct server *server);
void idle_manager_notify_activity(struct seat *seat);
/**
* idle inhibitor manager
*/
bool idle_inhibit_manager_create(struct server *server);
/**
* input method and text input
*/
bool input_method_manager_create(struct input_manager *input_manager);
void input_method_set_focus(struct seat *seat, struct wlr_surface *wlr_surface);
bool input_method_handle_key(struct keyboard *keyboard, uint32_t time, uint32_t key,
uint32_t state);
bool input_method_handle_modifiers(struct keyboard *keyboard);
bool keyboard_is_from_input_method(struct keyboard *keyboard);
/**
* selection drag icon
*/
bool selection_manager_create(struct input_manager *input_manager);
void selection_handle_cursor_move(struct seat *seat, int lx, int ly);
bool selection_is_dragging(struct seat *seat);
/**
* tablet manager
*/
struct wlr_tablet_tool_axis_event;
struct wlr_tablet_tool_proximity_event;
struct wlr_tablet_tool_tip_event;
struct wlr_tablet_tool_button_event;
bool tablet_manager_create(struct input_manager *input_manager);
void tablet_set_focus(struct seat *seat, struct wlr_surface *surface);
void tablet_handle_tool_axis(struct wlr_tablet_tool_axis_event *event);
bool tablet_handle_tool_proximity(struct wlr_tablet_tool_proximity_event *event);
bool tablet_handle_tool_tip(struct wlr_tablet_tool_tip_event *event);
bool tablet_handle_tool_button(struct wlr_tablet_tool_button_event *event);
bool tablet_has_implicit_grab(struct seat *seat);
/**
* touchscreen manager
*/
struct wlr_touch_up_event;
struct wlr_touch_down_event;
struct wlr_touch_motion_event;
struct wlr_touch_cancel_event;
bool touch_manager_create(struct input_manager *input_manager);
bool touch_handle_down(struct wlr_touch_down_event *event);
void touch_handle_up(struct wlr_touch_up_event *event, bool handle);
void touch_handle_motion(struct wlr_touch_motion_event *event, bool handle);
void touch_handle_cancel(struct wlr_touch_cancel_event *event, bool handle);
void touch_reset_gesture(struct input_manager *input_manager);
/**
* binding manager for keysym, gesture
*/
bool bindings_create(struct input_manager *input_manager);
struct key_binding *bindings_get_key_binding(struct keyboard_state *keyboard_state);
bool bindings_get_key_binding_bypass_grab(struct key_binding *binding);
bool bindings_get_key_binding_no_repeat(struct key_binding *binding);
bool bindings_handle_key_binding(struct key_binding *binding, bool *repeat);
bool bindings_handle_gesture_binding(struct gesture_state *gesture_state);
/**
* seat pointer and keyboard feed event
*/
void cursor_feed_motion(struct cursor *cursor, uint32_t time, struct wlr_input_device *device,
double dx, double dy, double dx_unaccel, double dy_unaccel);
void cursor_feed_button(struct cursor *cursor, uint32_t button, bool pressed, uint32_t time,
uint32_t double_click_time);
void cursor_feed_axis(struct cursor *cursor, uint32_t orientation, uint32_t source, double delta,
int32_t delta_discrete, int32_t relative_direction, uint32_t time);
/**
* seat pointer constraint
*/
struct cursor_constraint *cursor_constraint_create(struct cursor *cursor,
struct wlr_pointer_constraint_v1 *constraint);
void cursor_constraint_set_focus(struct seat *seat, struct wlr_surface *surface);
/**
* keeps track of the states of lock and modifier keys
*/
#if HAVE_KDE_KEYSTATE
bool kde_keystate_manager_create(struct input_manager *input_manager);
#else
static __attribute__((unused)) inline bool
kde_keystate_manager_create(struct input_manager *input_manager)
{
return false;
}
#endif
bool transient_seat_manager_create(struct input_manager *input_manager);
/**
* xdg toplevel drag support
*/
struct wlr_data_source;
bool toplevel_drag_manager_create(struct server *server);
bool toplevel_drag_move(struct wlr_data_source *source, int lx, int ly);
#endif /* _INPUT_P_H_ */
kylin-wayland-compositor/src/input/text_input_v1.h 0000664 0001750 0001750 00000003512 15160460057 021374 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#ifndef _TEXT_INPUT_V1_H_
#define _TEXT_INPUT_V1_H_
#include
#include
struct text_input_v1 {
struct wl_resource *resource;
struct wl_list link;
struct wlr_seat *seat;
struct wl_listener seat_destroy;
struct wlr_surface *surface;
struct wl_listener surface_destroy;
struct wlr_box cursor_rectangle;
struct {
bool pending;
char *text;
uint32_t cursor;
uint32_t anchor;
} surrounding;
struct {
bool pending;
uint32_t hint;
uint32_t purpose;
} content_type;
uint32_t serial;
bool activated;
struct {
struct wl_signal activate;
struct wl_signal deactivate;
struct wl_signal commit;
struct wl_signal destroy;
} events;
};
struct text_input_manager_v1 {
struct wl_global *global;
struct wl_list text_inputs;
struct wl_listener display_destroy;
struct {
struct wl_signal text_input;
struct wl_signal destroy;
} events;
};
struct text_input_manager_v1 *text_input_manager_v1_create(struct wl_display *display);
void text_input_v1_send_enter(struct text_input_v1 *text_input, struct wlr_surface *surface);
void text_input_v1_send_leave(struct text_input_v1 *text_input);
void text_input_v1_send_preedit_string(struct text_input_v1 *text_input, const char *text,
int32_t cursor_begin);
void text_input_v1_send_commit_string(struct text_input_v1 *text_input, const char *text);
void text_input_v1_send_delete_surrounding_text(struct text_input_v1 *text_input, const char *text,
uint32_t before_length, uint32_t after_length);
#endif /* _TEXT_INPUT_V1_H_ */
kylin-wayland-compositor/src/input/toplevel_drag.c 0000664 0001750 0001750 00000020112 15160460057 021400 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include "xdg-toplevel-drag-v1-protocol.h"
#include "input_p.h"
#include "server.h"
#include "view/view.h"
struct toplevel_drag_manager {
struct wl_global *global;
struct wl_list drags;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct toplevel_drag {
struct wl_resource *resource;
struct wl_list link;
struct wlr_data_source *source;
struct wl_listener source_destroy;
struct wlr_xdg_toplevel *toplevel;
struct wl_listener toplevel_unmap;
struct wl_listener toplevel_destroy;
int32_t off_x, off_y;
};
static struct toplevel_drag_manager *manager = NULL;
static struct view *toplevel_set_input_bypassed(struct wlr_xdg_toplevel *toplevel, bool bypassed)
{
if (!toplevel) {
return NULL;
}
struct view *view = view_try_from_wlr_surface(toplevel->base->surface);
if (!view) {
return NULL;
}
ky_scene_node_set_input_bypassed(&view->tree->node, bypassed);
return view;
}
static void toplevel_drag_destroy(struct toplevel_drag *drag)
{
toplevel_set_input_bypassed(drag->toplevel, false);
wl_list_remove(&drag->source_destroy.link);
wl_list_remove(&drag->toplevel_unmap.link);
wl_list_remove(&drag->toplevel_destroy.link);
wl_list_remove(&drag->link);
free(drag);
}
static void toplevel_drag_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void toplevel_drag_handle_attach(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *toplevel, int32_t x_offset,
int32_t y_offset)
{
struct toplevel_drag *drag = wl_resource_get_user_data(resource);
if (!drag) {
return;
}
if (drag->toplevel) {
wl_resource_post_error(resource, XDG_TOPLEVEL_DRAG_V1_ERROR_TOPLEVEL_ATTACHED,
"valid toplevel already attached");
return;
}
drag->toplevel = wlr_xdg_toplevel_from_resource(toplevel);
if (!drag->toplevel) {
return;
}
wl_list_remove(&drag->toplevel_unmap.link);
wl_signal_add(&drag->toplevel->base->surface->events.unmap, &drag->toplevel_unmap);
wl_list_remove(&drag->toplevel_destroy.link);
wl_signal_add(&drag->toplevel->base->events.destroy, &drag->toplevel_destroy);
drag->off_x = x_offset;
drag->off_y = y_offset;
}
static const struct xdg_toplevel_drag_v1_interface toplevel_drag_impl = {
.destroy = toplevel_drag_handle_destroy,
.attach = toplevel_drag_handle_attach,
};
static void toplevel_drag_handle_resource_destroy(struct wl_resource *resource)
{
struct toplevel_drag *drag = wl_resource_get_user_data(resource);
if (drag) {
toplevel_drag_destroy(drag);
}
}
static void toplevel_drag_handle_source_destroy(struct wl_listener *listener, void *data)
{
struct toplevel_drag *drag = wl_container_of(listener, drag, source_destroy);
wl_resource_set_user_data(drag->resource, NULL);
toplevel_drag_destroy(drag);
}
static void toplevel_drag_reset_toplevel(struct toplevel_drag *drag)
{
wl_list_remove(&drag->toplevel_unmap.link);
wl_list_remove(&drag->toplevel_destroy.link);
wl_list_init(&drag->toplevel_unmap.link);
wl_list_init(&drag->toplevel_destroy.link);
toplevel_set_input_bypassed(drag->toplevel, false);
drag->toplevel = NULL;
}
static void toplevel_drag_handle_toplevel_unmap(struct wl_listener *listener, void *data)
{
struct toplevel_drag *drag = wl_container_of(listener, drag, toplevel_unmap);
toplevel_drag_reset_toplevel(drag);
}
static void toplevel_drag_handle_toplevel_destroy(struct wl_listener *listener, void *data)
{
struct toplevel_drag *drag = wl_container_of(listener, drag, toplevel_destroy);
toplevel_drag_reset_toplevel(drag);
}
static void manager_handle_get_toplevel_drag(struct wl_client *client, struct wl_resource *resource,
uint32_t id, struct wl_resource *data_source)
{
// trick to get wlr_data_source as wlr_client_data_source is private
struct wlr_data_source *source = wl_resource_get_user_data(data_source);
if (!source) {
wl_client_post_implementation_error(client, "invalid data source");
return;
}
struct toplevel_drag *drag = calloc(1, sizeof(*drag));
if (!drag) {
wl_client_post_no_memory(client);
return;
}
int version = wl_resource_get_version(resource);
drag->resource = wl_resource_create(client, &xdg_toplevel_drag_v1_interface, version, id);
if (!drag->resource) {
free(drag);
wl_client_post_no_memory(client);
return;
}
wl_list_insert(&manager->drags, &drag->link);
wl_resource_set_implementation(drag->resource, &toplevel_drag_impl, drag,
toplevel_drag_handle_resource_destroy);
drag->source = source;
drag->source_destroy.notify = toplevel_drag_handle_source_destroy;
wl_signal_add(&source->events.destroy, &drag->source_destroy);
drag->toplevel_unmap.notify = toplevel_drag_handle_toplevel_unmap;
wl_list_init(&drag->toplevel_unmap.link);
drag->toplevel_destroy.notify = toplevel_drag_handle_toplevel_destroy;
wl_list_init(&drag->toplevel_destroy.link);
}
static void manager_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct xdg_toplevel_drag_manager_v1_interface toplevel_drag_manager_impl = {
.destroy = manager_handle_destroy,
.get_xdg_toplevel_drag = manager_handle_get_toplevel_drag,
};
static void toplevel_drag_manager_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &xdg_toplevel_drag_manager_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &toplevel_drag_manager_impl, manager, NULL);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->server_destroy.link);
free(manager);
manager = NULL;
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
}
bool toplevel_drag_manager_create(struct server *server)
{
manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->global = wl_global_create(server->display, &xdg_toplevel_drag_manager_v1_interface, 1,
manager, toplevel_drag_manager_bind);
if (!manager->global) {
kywc_log(KYWC_WARN, "Failed to create xdg_toplevel_drag_manager_v1");
free(manager);
manager = NULL;
return false;
}
wl_list_init(&manager->drags);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &manager->server_destroy);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &manager->display_destroy);
return true;
}
static struct toplevel_drag *toplevel_drag_from_data_source(struct wlr_data_source *source)
{
struct toplevel_drag *drag;
wl_list_for_each(drag, &manager->drags, link) {
if (drag->source == source) {
return drag;
}
}
return NULL;
}
bool toplevel_drag_move(struct wlr_data_source *source, int lx, int ly)
{
if (!manager || !source) {
return false;
}
struct toplevel_drag *drag = toplevel_drag_from_data_source(source);
if (!drag || !drag->toplevel) {
return false;
}
struct view *view = toplevel_set_input_bypassed(drag->toplevel, true);
if (view) {
view_do_move(view, lx - drag->off_x, ly - drag->off_y);
}
return true;
}
kylin-wayland-compositor/src/input/binding.c 0000664 0001750 0001750 00000057644 15160461067 020211 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include
#include "input_p.h"
#include "server.h"
#include "util/macros.h"
#include "util/string.h"
struct key_binding {
struct wl_list link;
uint32_t modifiers;
uint32_t keysym;
bool no_repeat;
bool bypass_grab;
char *keybind;
char *desc;
void (*action)(struct key_binding *binding, void *data);
void *data;
};
struct keysyms_binding {
struct wl_list bindings; /* key binding */
};
struct gesture_binding {
struct wl_list link;
enum gesture_type type;
enum gesture_stage stage;
uint8_t fingers;
uint32_t devices;
uint32_t directions;
uint32_t follow_direction;
uint32_t edges;
double follow_threshold;
char *desc;
void (*action)(struct gesture_binding *binding, void *data, double dx, double dy);
void *data;
};
static struct bindings {
struct keysyms_binding keysyms_binding[KEY_BINDING_TYPE_NUM];
struct wl_list gesture_bindings;
struct wl_listener server_destroy;
size_t keysym_bindings_block;
size_t type_nlocks[KEY_BINDING_TYPE_NUM];
uint32_t keysyms_binding_masks; /* binding mask */
} *bindings = NULL;
const struct key_binding_type2string {
enum key_binding_type type;
const char *name;
} key_binding_table[] = {
{ KEY_BINDING_TYPE_CUSTOM_DEF, "WLCOM_CUSTOM_DEF" },
{ KEY_BINDING_TYPE_WIN_MENU, "WLCOM_WIN_MENU" },
{ KEY_BINDING_TYPE_SWITCH_WORKSPACE, "WLCOM_SWITCH_WORKSPACE" },
{ KEY_BINDING_TYPE_WINDOW_ACTION_MINIMIZE, "WLCOM_WINDOW_ACTION_MINIMIZE" },
{ KEY_BINDING_TYPE_WINDOW_ACTION_MAXIMIZE, "WLCOM_WINDOW_ACTION_MAXIMIZE" },
{ KEY_BINDING_TYPE_WINDOW_ACTION_CLOSE, "WLCOM_WINDOW_ACTION_CLOSE" },
{ KEY_BINDING_TYPE_WINDOW_ACTION_MENU, "WLCOM_WINDOW_ACTION_MENU" },
{ KEY_BINDING_TYPE_WINDOW_ACTION_TILED, "WLCOM_WINDOW_ACTION_TILED" },
{ KEY_BINDING_TYPE_WINDOW_ACTION_OUTPUT, "WLCOM_WINDOW_ACTION_OUTPUT" },
{ KEY_BINDING_TYPE_WINDOW_ACTION_SEND, "WLCOM_WINDOW_ACTION_SEND" },
{ KEY_BINDING_TYPE_WINDOW_ACTION_CAPTURE, "WLCOM_WINDOW_ACTION_CAPTURE" },
{ KEY_BINDING_TYPE_MAXIMIZED_VIEWS, "WLCOM_MAXIMIZED_VIEWS" },
{ KEY_BINDING_TYPE_TOGGLE_SHOW_DESKTOP, "WLCOM_TOGGLE_SHOW_DESKTOP" },
{ KEY_BINDING_TYPE_SHOW_DESKTOP, "WLCOM_SHOW_DESKTOP" },
{ KEY_BINDING_TYPE_RESTORE_DESKTOP, "WLCOM_RESTORE_DESKTOP" },
{ KEY_BINDING_TYPE_TOGGLE_SHOW_VIEWS, "WLCOM_TOGGLE_SHOW_VIEWS" },
{ KEY_BINDING_TYPE_TOGGLE_SHOW_WINDOWS, "WLCOM_TOGGLE_SHOW_WINDOWS" },
{ KEY_BINDING_TYPE_COLOR_FILTER, "WLCOM_COLOR_FILTER" },
{ KEY_BINDING_TYPE_ZOOM, "WLCOM_ZOOM" },
{ KEY_BINDING_TYPE_NUM, "WLCOM_ALL" },
};
struct key_binding *kywc_key_binding_create(const char *keybind, const char *desc)
{
struct key_binding *binding = calloc(1, sizeof(*binding));
if (!binding) {
return NULL;
}
/* check no_repeat first */
size_t length = 0;
char **bind_str = string_split(keybind, ":", &length);
binding->no_repeat = length == 2 && strcmp(bind_str[1], "no") == 0;
size_t len = 0;
char **split_str = string_split(length == 2 ? bind_str[0] : keybind, "+", &len);
string_free_split(bind_str);
for (size_t i = 0; i < len; i++) {
uint32_t mod = keyboard_get_modifier_mask_by_name(split_str[i]);
if (mod) {
binding->modifiers |= mod;
continue;
}
/* whatever keep the lower keysym */
xkb_keysym_t sym = xkb_keysym_from_name(split_str[i], XKB_KEYSYM_CASE_INSENSITIVE);
binding->keysym = xkb_keysym_to_lower(sym);
}
string_free_split(split_str);
if (desc) {
binding->desc = strdup(desc);
}
binding->keybind = strdup(keybind);
wl_list_init(&binding->link);
kywc_log(KYWC_DEBUG, "Keybind %s: %s", keybind, desc);
return binding;
}
struct key_binding *kywc_key_binding_create_by_symbol(unsigned int keysym, unsigned int modifiers,
bool no_repeat, bool bypass_grab,
const char *desc)
{
struct key_binding *binding = calloc(1, sizeof(*binding));
if (!binding) {
return NULL;
}
binding->modifiers = modifiers;
binding->keysym = keysym;
binding->no_repeat = no_repeat;
binding->bypass_grab = bypass_grab;
if (desc) {
binding->desc = strdup(desc);
}
char name[64];
if (xkb_keysym_get_name(keysym, name, sizeof(name)) > 0) {
const char *mods = keyboard_get_modifier_names(modifiers, '+');
binding->keybind = string_create("%s+%s", mods, name);
}
wl_list_init(&binding->link);
return binding;
}
void kywc_key_binding_destroy(struct key_binding *binding)
{
wl_list_remove(&binding->link);
free(binding->keybind);
free(binding->desc);
free(binding);
}
static bool key_binding_is_valid(struct key_binding *binding, uint32_t keysym, uint32_t modifiers)
{
for (int i = 0; i < KEY_BINDING_TYPE_NUM; ++i) {
struct key_binding *bind;
struct keysyms_binding *keysyms_binding = &bindings->keysyms_binding[i];
wl_list_for_each(bind, &keysyms_binding->bindings, link) {
/* skip itself */
if (bind == binding) {
continue;
}
if (!(modifiers ^ bind->modifiers) && keysym == bind->keysym) {
kywc_log(KYWC_WARN, "Keybind %s(%s) is already registered by %s(%s)",
binding->keybind, binding->desc, bind->keybind, bind->desc);
return false;
}
}
}
return true;
}
bool kywc_key_binding_register(struct key_binding *binding, enum key_binding_type type,
void (*action)(struct key_binding *binding, void *data), void *data)
{
if (kywc_key_binding_is_registered(binding)) {
return true;
}
if (!key_binding_is_valid(binding, binding->keysym, binding->modifiers)) {
return false;
}
struct keysyms_binding *keysyms_binding = &bindings->keysyms_binding[type];
wl_list_insert(&keysyms_binding->bindings, &binding->link);
/* Make sure the type is unblocking */
if (!bindings->type_nlocks[type]) {
bindings->keysyms_binding_masks |= 1 << type;
}
if (action) {
binding->action = action;
}
if (data) {
binding->data = data;
}
return true;
}
bool kywc_key_binding_update(struct key_binding *binding, unsigned int keysym,
unsigned int modifiers, const char *desc)
{
if (kywc_key_binding_is_registered(binding) &&
!key_binding_is_valid(binding, keysym, modifiers)) {
return false;
}
binding->keysym = keysym;
binding->modifiers = modifiers;
if (desc) {
free(binding->desc);
binding->desc = strdup(desc);
}
return true;
}
void kywc_key_binding_unregister(struct key_binding *binding)
{
wl_list_remove(&binding->link);
wl_list_init(&binding->link);
}
bool kywc_key_binding_is_registered(struct key_binding *binding)
{
return !wl_list_empty(&binding->link);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&bindings->server_destroy.link);
for (size_t i = 0; i < KEY_BINDING_TYPE_NUM; ++i) {
struct keysyms_binding *keysyms_binding = &bindings->keysyms_binding[i];
struct key_binding *key_binding, *key_binding_tmp;
wl_list_for_each_safe(key_binding, key_binding_tmp, &keysyms_binding->bindings, link) {
kywc_key_binding_destroy(key_binding);
}
}
struct gesture_binding *gesture_binding, *gesture_binding_tmp;
wl_list_for_each_safe(gesture_binding, gesture_binding_tmp, &bindings->gesture_bindings, link) {
kywc_gesture_binding_destroy(gesture_binding);
}
free(bindings);
bindings = NULL;
}
bool bindings_create(struct input_manager *input_manager)
{
bindings = calloc(1, sizeof(*bindings));
if (!bindings) {
return false;
}
for (size_t i = 0; i < KEY_BINDING_TYPE_NUM; ++i) {
struct keysyms_binding *keysyms_binding = &bindings->keysyms_binding[i];
wl_list_init(&keysyms_binding->bindings);
}
wl_list_init(&bindings->gesture_bindings);
bindings->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(input_manager->server, &bindings->server_destroy);
return true;
}
static bool match_key_binding(struct keyboard_state *keyboard_state, struct key_binding *binding)
{
for (size_t j = 0; j < keyboard_state->npressed; j++) {
uint32_t keysym = keyboard_state->pressed_keysyms[j];
if (binding->keysym == xkb_keysym_to_lower(keysym)) {
return true;
}
}
return false;
}
struct key_binding *bindings_get_key_binding(struct keyboard_state *keyboard_state)
{
if (bindings->keysym_bindings_block) {
return NULL;
}
for (size_t i = 0; i < KEY_BINDING_TYPE_NUM; ++i) {
if (!(bindings->keysyms_binding_masks & (1 << i))) {
continue;
}
struct key_binding *binding;
struct keysyms_binding *keysyms_binding = &bindings->keysyms_binding[i];
wl_list_for_each(binding, &keysyms_binding->bindings, link) {
if ((keyboard_state->only_one_modifier && binding->keysym) ||
(!keyboard_state->only_one_modifier && !binding->keysym)) {
continue;
}
if (keyboard_state->last_modifiers ^ binding->modifiers) {
continue;
}
if (keyboard_state->npressed < 1 && binding->keysym) {
continue;
}
if (!binding->keysym || match_key_binding(keyboard_state, binding)) {
return binding;
}
}
}
return NULL;
}
bool bindings_get_key_binding_bypass_grab(struct key_binding *binding)
{
if (!binding) {
return false;
}
return binding->bypass_grab;
}
bool bindings_get_key_binding_no_repeat(struct key_binding *binding)
{
if (!binding) {
return false;
}
return binding->no_repeat;
}
bool bindings_handle_key_binding(struct key_binding *binding, bool *repeat)
{
if (!binding) {
*repeat = false;
return false;
}
if (binding->action) {
binding->action(binding, binding->data);
}
*repeat = !binding->no_repeat;
return true;
}
void kywc_gesture_binding_destroy(struct gesture_binding *binding)
{
wl_list_remove(&binding->link);
free(binding->desc);
free(binding);
}
static bool gesture_binding_is_valid(struct gesture_binding *binding, enum gesture_type type,
enum gesture_stage stage, uint32_t devices,
uint32_t directions, uint32_t follow_direction,
uint8_t fingers, uint8_t edges)
{
struct gesture_binding *bind;
wl_list_for_each(bind, &bindings->gesture_bindings, link) {
/* skip itself */
if (bind == binding) {
continue;
}
if (bind->type == type && (bind->devices & devices) && (bind->stage == stage) &&
(GESTURE_DIRECTION_NONE == bind->directions || (bind->directions & directions)) &&
(GESTURE_EDGE_NONE == bind->edges || (edges & bind->edges)) &&
bind->fingers == fingers && bind->follow_direction == follow_direction) {
return false;
}
}
return true;
}
static uint32_t gesture_string_parse_directions(const char *str)
{
uint32_t directions = GESTURE_DIRECTION_NONE;
size_t len = 0;
char **split_str = string_split(str, "+", &len);
for (size_t i = 0; i < len; i++) {
if (strcmp(split_str[i], "none") == 0) {
directions |= GESTURE_DIRECTION_NONE;
} else if (strcmp(split_str[i], "up") == 0) {
directions |= GESTURE_DIRECTION_UP;
} else if (strcmp(split_str[i], "down") == 0) {
directions |= GESTURE_DIRECTION_DOWN;
} else if (strcmp(split_str[i], "left") == 0) {
directions |= GESTURE_DIRECTION_LEFT;
} else if (strcmp(split_str[i], "right") == 0) {
directions |= GESTURE_DIRECTION_RIGHT;
} else if (strcmp(split_str[i], "inward") == 0) {
directions |= GESTURE_DIRECTION_INWARD;
} else if (strcmp(split_str[i], "outward") == 0) {
directions |= GESTURE_DIRECTION_OUTWARD;
} else if (strcmp(split_str[i], "clockwise") == 0) {
directions |= GESTURE_DIRECTION_CLOCKWISE;
} else if (strcmp(split_str[i], "counterclockwise") == 0) {
directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
} else {
kywc_log(KYWC_WARN, "Expected directions, got %s", str);
}
}
string_free_split(split_str);
return directions;
}
static uint32_t gesture_string_parse_edges(const char *str)
{
uint32_t edges = GESTURE_EDGE_NONE;
size_t len = 0;
char **split_str = string_split(str, "+", &len);
for (size_t i = 0; i < len; i++) {
if (strcmp(split_str[i], "none") == 0) {
edges |= GESTURE_EDGE_NONE;
} else if (strcmp(split_str[i], "top") == 0) {
edges |= GESTURE_EDGE_TOP;
} else if (strcmp(split_str[i], "bottom") == 0) {
edges |= GESTURE_EDGE_BOTTOM;
} else if (strcmp(split_str[i], "left") == 0) {
edges |= GESTURE_EDGE_LEFT;
} else if (strcmp(split_str[i], "right") == 0) {
edges |= GESTURE_EDGE_RIGHT;
} else {
kywc_log(KYWC_WARN, "Expected edges, got %s", str);
}
}
string_free_split(split_str);
return edges;
}
struct gesture_binding *kywc_gesture_binding_create_by_string(const char *gestures,
const char *desc)
{
enum gesture_type type;
uint8_t fingers;
uint32_t devices;
uint32_t directions;
uint32_t edges = GESTURE_EDGE_NONE;
enum gesture_stage stage = GESTURE_STAGE_TRIGGER;
double follow_threshold = 0.0;
uint32_t follow_direction = GESTURE_DIRECTION_NONE;
size_t len = 0;
char **split_str = string_split(gestures, ":", &len);
if (len != 4 && len != 5 && len != 6 && len != 7 && len != 8) {
kywc_log(KYWC_ERROR,
"Expected :::"
"[:edges][:stage][:follow_threshold][:follow_direction],"
" got %s",
gestures);
goto err;
}
// type
if (strcmp(split_str[0], "tap") == 0) {
#if HAVE_LIBINPUT_RAW_TAP
type = GESTURE_TYPE_TAP;
#else
type = GESTURE_TYPE_HOLD;
#endif
} else if (strcmp(split_str[0], "hold") == 0) {
type = GESTURE_TYPE_HOLD;
} else if (strcmp(split_str[0], "pinch") == 0) {
type = GESTURE_TYPE_PINCH;
} else if (strcmp(split_str[0], "swipe") == 0) {
type = GESTURE_TYPE_SWIPE;
} else {
kywc_log(KYWC_ERROR, "Expected hold|pinch|swipe, got %s", gestures);
goto err;
}
// device
if (strcmp(split_str[1], "any") == 0) {
devices = GESTURE_DEVICE_TOUCHPAD | GESTURE_DEVICE_TOUCHSCREEN;
} else if (strcmp(split_str[1], "touch") == 0) {
devices = GESTURE_DEVICE_TOUCHSCREEN;
} else if (strcmp(split_str[1], "touchpad") == 0) {
devices = GESTURE_DEVICE_TOUCHPAD;
} else {
kywc_log(KYWC_ERROR, "Expected any|touch|touchpad, got %s", gestures);
goto err;
}
/* fingers: 1 - 9 */
/* directions: up down left right inward outward clockwise counterclockwise */
if ('1' <= split_str[2][0] && split_str[2][0] <= '9') {
fingers = atoi(split_str[2]);
directions = gesture_string_parse_directions(split_str[3]);
} else {
kywc_log(KYWC_ERROR, "Expected 1 - 9, got %s", gestures);
goto err;
}
/* edge: top bottom left right */
/* stage: before trigger after stop */
switch (len) {
case 8:
follow_direction = gesture_string_parse_directions(split_str[7]);
// fallthrough
case 7:
follow_threshold = atof(split_str[6]);
// fallthrough
case 6:
if (strcmp(split_str[5], "before") == 0) {
stage = GESTURE_STAGE_BEFORE;
} else if (strcmp(split_str[5], "trigger") == 0) {
stage = GESTURE_STAGE_TRIGGER;
} else if (strcmp(split_str[5], "after") == 0) {
stage = GESTURE_STAGE_AFTER;
} else if (strcmp(split_str[5], "stop") == 0) {
stage = GESTURE_STAGE_STOP;
} else {
kywc_log(KYWC_ERROR, "Expected before|trigger|after|stop, got %s", gestures);
goto err;
}
// fallthrough
case 5:
edges = gesture_string_parse_edges(split_str[4]);
break;
}
string_free_split(split_str);
kywc_log(KYWC_DEBUG, "Gesture binding: %s", gestures);
return kywc_gesture_binding_create(type, stage, devices, directions, edges, fingers,
follow_direction, follow_threshold, desc);
err:
string_free_split(split_str);
return NULL;
}
static bool gesture_checked(enum gesture_type type, uint32_t devices, uint32_t directions,
uint32_t edges, uint8_t fingers)
{
/* gesture type、devices and fingers cannot be 0 */
if (type == GESTURE_TYPE_NONE || devices == GESTURE_DEVICE_NONE || fingers == 0) {
return false;
}
/* pinch gesture fingers require no less than 2 */
if (type == GESTURE_TYPE_PINCH && fingers < 2) {
return false;
}
/* pinch or touchpad all gestures edges need to be 0 */
if ((type == GESTURE_TYPE_PINCH || devices & GESTURE_DEVICE_TOUCHPAD) &&
edges != GESTURE_EDGE_NONE) {
return false;
}
/* the number of fingers for touchpad gestures need to be greater than 2 */
if (type == GESTURE_TYPE_TAP && devices & GESTURE_DEVICE_TOUCHPAD && fingers < 3) {
return false;
}
/* the number of fingers for touchpad swipe gestures need to be greater than 1 */
if (type == GESTURE_TYPE_SWIPE && devices & GESTURE_DEVICE_TOUCHPAD && fingers == 1) {
return false;
}
/* the edge of the swipe gesture for one finger of the touchscreen cannot be 0 */
if (type == GESTURE_TYPE_SWIPE && edges == GESTURE_EDGE_NONE && fingers == 1) {
return false;
}
/* swipe gesture from edge, the start edges and directions need to be set properly */
if (edges != GESTURE_EDGE_NONE &&
((directions & GESTURE_DIRECTION_LEFT && !(edges & GESTURE_EDGE_RIGHT)) ||
(directions & GESTURE_DIRECTION_RIGHT && !(edges & GESTURE_EDGE_LEFT)) ||
(directions & GESTURE_DIRECTION_UP && !(edges & GESTURE_EDGE_BOTTOM)) ||
(directions & GESTURE_DIRECTION_DOWN && !(edges & GESTURE_EDGE_TOP)))) {
return false;
}
return true;
}
struct gesture_binding *kywc_gesture_binding_create(enum gesture_type type,
enum gesture_stage stage, uint32_t devices,
uint32_t directions, uint32_t edges,
uint8_t fingers, uint32_t follow_direction,
double follow_threshold, const char *desc)
{
if (!gesture_checked(type, devices, directions, edges, fingers)) {
kywc_log(KYWC_ERROR, "Gesture checks are illega");
return NULL;
}
struct gesture_binding *binding = calloc(1, sizeof(*binding));
if (!binding) {
return NULL;
}
binding->type = type;
binding->stage = stage;
binding->devices = devices;
binding->directions = directions;
binding->fingers = fingers;
binding->edges = edges;
binding->follow_threshold = follow_threshold > 0.0 ? follow_threshold : 0.0;
binding->follow_direction = follow_direction;
if (desc) {
binding->desc = strdup(desc);
}
wl_list_init(&binding->link);
return binding;
}
bool kywc_gesture_binding_register(struct gesture_binding *binding,
void (*action)(struct gesture_binding *binding, void *data,
double dx, double dy),
void *data)
{
if (!wl_list_empty(&binding->link)) {
return true;
}
if (!gesture_binding_is_valid(binding, binding->type, binding->stage, binding->devices,
binding->directions, binding->follow_direction, binding->fingers,
binding->edges)) {
return false;
}
wl_list_insert(&bindings->gesture_bindings, &binding->link);
binding->action = action;
binding->data = data;
return true;
}
bool bindings_handle_gesture_binding(struct gesture_state *gesture_state)
{
struct gesture_binding *binding;
wl_list_for_each(binding, &bindings->gesture_bindings, link) {
if (gesture_state->type != binding->type) {
continue;
}
if (gesture_state->fingers != binding->fingers) {
continue;
}
if (gesture_state->stage != binding->stage) {
continue;
}
/* do not trigger follow if interval < follow_threshold */
if (gesture_state->stage == GESTURE_STAGE_BEFORE ||
gesture_state->stage == GESTURE_STAGE_AFTER) {
if ((gesture_state->follow_direction != binding->follow_direction) ||
(gesture_state->follow_dx > -binding->follow_threshold &&
gesture_state->follow_dx < binding->follow_threshold &&
gesture_state->follow_dy > -binding->follow_threshold &&
gesture_state->follow_dy < binding->follow_threshold)) {
continue;
}
}
if ((gesture_state->device & binding->devices) &&
(binding->directions == GESTURE_DIRECTION_NONE ||
binding->stage == GESTURE_STAGE_BEFORE ||
gesture_state->directions & binding->directions) &&
(binding->edges == GESTURE_EDGE_NONE || gesture_state->edge & binding->edges)) {
kywc_log(KYWC_DEBUG, "Start gesture binding: %s", binding->desc);
if (binding->action) {
binding->action(binding, binding->data, gesture_state->dx, gesture_state->dy);
}
return true;
}
}
return false;
}
void kywc_key_binding_for_each(binding_iterator_func_t iterator)
{
for (size_t i = 0; i < KEY_BINDING_TYPE_NUM; ++i) {
struct key_binding *binding;
struct keysyms_binding *keysyms_binding = &bindings->keysyms_binding[i];
wl_list_for_each(binding, &keysyms_binding->bindings, link) {
if (iterator(binding, binding->keybind, binding->desc, binding->modifiers,
binding->keysym)) {
return;
}
}
}
}
void kywc_key_binding_block_all(bool block)
{
if (block) {
bindings->keysym_bindings_block++;
} else {
assert(bindings->keysym_bindings_block > 0);
bindings->keysym_bindings_block--;
}
}
void kywc_key_binding_block_type(enum key_binding_type type, bool block)
{
if (block) {
bindings->keysyms_binding_masks &= ~(1 << type);
bindings->type_nlocks[type]++;
} else {
assert(bindings->type_nlocks[type] > 0);
bindings->type_nlocks[type]--;
if (!bindings->type_nlocks[type]) {
bindings->keysyms_binding_masks |= (1 << type);
}
}
}
enum key_binding_type kywc_key_binding_type_by_name(const char *name, bool *found)
{
for (size_t i = 0; i < ARRAY_SIZE(key_binding_table); ++i) {
if (strcmp(name, key_binding_table[i].name)) {
continue;
}
if (found) {
*found = true;
}
return key_binding_table[i].type;
}
if (found) {
*found = false;
}
return KEY_BINDING_TYPE_CUSTOM_DEF;
}
kylin-wayland-compositor/src/input/gesture.c 0000664 0001750 0001750 00000021033 15160461067 020234 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include "input/gesture.h"
#include "input_p.h"
#define GESTURE_DEFAULT_TIMEOUT (200)
#define GESTURE_TOUCHPAD_TIMEOUT (20)
#define GESTURE_TOUCHPAD_TAP_TIMEOUT (1)
#define GESTURE_TOUCHSCREEN_HOLD_TIMEOUT (800)
/**
* touchpad:
* Relative motion deltas are normalized to represent those of a device with 1000dpi resolution
* https://wayland.freedesktop.org/libinput/doc/latest/api/group__event__gesture.html#ga3888052854155ad133fa837e4f28d771
* some devices are below 1000dpi, so we chase kwin, default 50
*/
#define GESTURE_TOUCHPAD_TRIGGER_THRESHOLD (10)
/* touchscreen: chase kwin, full width height is 1 */
#define GESTURE_TOUCHSCREEN_TRIGGER_THRESHOLD (0.15)
static char *gestures[] = { "none", "tap", "pinch", "swipe", "hold" };
static void gesture_state_reset(struct gesture_state *state)
{
state->type = GESTURE_TYPE_NONE;
state->stage = GESTURE_STAGE_NONE;
state->device = GESTURE_DEVICE_NONE;
state->directions = GESTURE_DIRECTION_NONE;
state->follow_direction = GESTURE_DIRECTION_NONE;
state->edge = GESTURE_EDGE_NONE;
state->triggered = false;
state->handled = false;
if (state->timer) {
wl_event_source_timer_update(state->timer, 0);
}
}
static void gesture_state_trigger(struct gesture_state *state)
{
// Ignore gesture under some threshold
const double min_rotation = 5;
const double min_scale_delta = 0.1;
// Determine direction
switch (state->type) {
// Gestures with scale and rotation
case GESTURE_TYPE_PINCH:
if (state->rotation > min_rotation) {
state->directions |= GESTURE_DIRECTION_CLOCKWISE;
}
if (state->rotation < -min_rotation) {
state->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
}
if (state->scale > (1.0 + min_scale_delta)) {
state->directions |= GESTURE_DIRECTION_OUTWARD;
}
if (state->scale < (1.0 - min_scale_delta)) {
state->directions |= GESTURE_DIRECTION_INWARD;
}
// fallthrough
// Gestures with dx and dy
case GESTURE_TYPE_SWIPE:
if (fabs(state->dx) > fabs(state->dy)) {
if (state->dx > 0) {
state->directions |= GESTURE_DIRECTION_RIGHT;
} else {
state->directions |= GESTURE_DIRECTION_LEFT;
}
} else {
if (state->dy > 0) {
state->directions |= GESTURE_DIRECTION_DOWN;
} else {
state->directions |= GESTURE_DIRECTION_UP;
}
}
// Gesture without any direction
case GESTURE_TYPE_HOLD:
case GESTURE_TYPE_TAP:
break;
// Not tracking any gesture
case GESTURE_TYPE_NONE:
assert("Not tracking any gesture.");
break;
}
kywc_log(KYWC_DEBUG, "Gesture %s triggered: fingers: %u, directions: %d", gestures[state->type],
state->fingers, state->directions);
state->triggered = true;
state->stage = GESTURE_STAGE_TRIGGER;
state->follow_dx = 0.0;
state->follow_dy = 0.0;
state->follow_direction = GESTURE_DIRECTION_NONE;
state->handled = bindings_handle_gesture_binding(state);
}
static void gesture_state_follow(struct gesture_state *state, double dx, double dy,
bool before_triggered)
{
// only support swipe for now
if (state->type != GESTURE_TYPE_SWIPE) {
return;
}
state->follow_dx += dx;
state->follow_dy += dy;
if (fabs(dx) > fabs(dy)) {
if (dx > 0) {
state->follow_direction = GESTURE_DIRECTION_RIGHT;
} else {
state->follow_direction = GESTURE_DIRECTION_LEFT;
}
} else {
if (dy > 0) {
state->follow_direction = GESTURE_DIRECTION_DOWN;
} else {
state->follow_direction = GESTURE_DIRECTION_UP;
}
}
state->stage = before_triggered ? GESTURE_STAGE_BEFORE : GESTURE_STAGE_AFTER;
state->handled = bindings_handle_gesture_binding(state);
if (state->handled) {
state->follow_dx = 0.0;
state->follow_dy = 0.0;
}
}
static void gesture_state_stop(struct gesture_state *state)
{
// only support swipe for now
if (state->type != GESTURE_TYPE_SWIPE) {
return;
}
state->stage = GESTURE_STAGE_STOP;
state->handled = bindings_handle_gesture_binding(state);
}
static int gesture_handle_timer(void *data)
{
struct gesture_state *state = data;
gesture_state_trigger(state);
return 0;
}
static void gesture_swipe_state_update(struct gesture_state *state, double dx, double dy)
{
state->dx += dx;
state->dy += dy;
if (!state->triggered) {
if ((state->device == GESTURE_DEVICE_TOUCHSCREEN &&
(state->dx < -GESTURE_TOUCHSCREEN_TRIGGER_THRESHOLD ||
state->dx > GESTURE_TOUCHSCREEN_TRIGGER_THRESHOLD ||
state->dy < -GESTURE_TOUCHSCREEN_TRIGGER_THRESHOLD ||
state->dy > GESTURE_TOUCHSCREEN_TRIGGER_THRESHOLD)) ||
(state->device == GESTURE_DEVICE_TOUCHPAD &&
(state->dx < -GESTURE_TOUCHPAD_TRIGGER_THRESHOLD ||
state->dx > GESTURE_TOUCHPAD_TRIGGER_THRESHOLD ||
state->dy < -GESTURE_TOUCHPAD_TRIGGER_THRESHOLD ||
state->dy > GESTURE_TOUCHPAD_TRIGGER_THRESHOLD))) {
gesture_state_trigger(state);
} else {
gesture_state_follow(state, dx, dy, true);
}
} else {
gesture_state_follow(state, dx, dy, false);
}
}
void gesture_state_init(struct gesture_state *state, void *display)
{
struct wl_event_loop *loop = wl_display_get_event_loop(display);
state->timer = wl_event_loop_add_timer(loop, gesture_handle_timer, state);
gesture_state_reset(state);
}
void gesture_state_finish(struct gesture_state *state)
{
if (state->timer) {
wl_event_source_remove(state->timer);
}
}
void gesture_state_begin(struct gesture_state *state, enum gesture_type type,
enum gesture_device device, enum gesture_edge edge, uint8_t fingers)
{
state->type = type;
state->device = device;
state->fingers = fingers;
state->dx = 0.0;
state->dy = 0.0;
state->scale = 1.0;
state->rotation = 0.0;
state->edge = edge;
if (state->timer && state->type != GESTURE_TYPE_SWIPE) {
int timeout = GESTURE_DEFAULT_TIMEOUT;
if (state->device == GESTURE_DEVICE_TOUCHPAD) {
timeout = state->type == GESTURE_TYPE_TAP ? GESTURE_TOUCHPAD_TAP_TIMEOUT
: GESTURE_TOUCHPAD_TIMEOUT;
} else if (state->type == GESTURE_TYPE_HOLD &&
state->device == GESTURE_DEVICE_TOUCHSCREEN) {
timeout = GESTURE_TOUCHSCREEN_HOLD_TIMEOUT;
}
wl_event_source_timer_update(state->timer, timeout);
}
kywc_log(KYWC_DEBUG, "Gesture %s state begin: fingers: %u", gestures[type], fingers);
}
void gesture_state_update(struct gesture_state *state, enum gesture_type type,
enum gesture_device device, double dx, double dy, double scale,
double rotation)
{
if (state->type != type || state->device != device) {
return;
}
if (state->type == GESTURE_TYPE_HOLD || state->type == GESTURE_TYPE_TAP) {
assert("tap or hold does not update.");
return;
}
if (state->type == GESTURE_TYPE_PINCH) {
state->dx += dx;
state->dy += dy;
state->scale = scale;
state->rotation += rotation;
}
if (state->type == GESTURE_TYPE_SWIPE) {
gesture_swipe_state_update(state, dx, dy);
}
kywc_log(KYWC_DEBUG, "gesture %s state update: fingers: %u gesture: %f %f %f %f",
gestures[state->type], state->fingers, state->dx, state->dy, state->scale,
state->rotation);
}
bool gesture_state_end(struct gesture_state *state, enum gesture_type type,
enum gesture_device device, bool cancelled)
{
if (state->type != type || state->device != device) {
return false;
}
if (cancelled) {
kywc_log(KYWC_DEBUG, "gesture %s state cancelled", gestures[state->type]);
gesture_state_stop(state);
gesture_state_reset(state);
return false;
}
if (!state->triggered && state->type == GESTURE_TYPE_PINCH) {
gesture_state_trigger(state);
}
bool handled = state->handled;
gesture_state_stop(state);
gesture_state_reset(state);
return handled;
}
kylin-wayland-compositor/src/input/seat.c 0000664 0001750 0001750 00000035341 15160461067 017521 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include
#include "input/keyboard.h"
#include "input/seat.h"
#include "input_p.h"
#include "server.h"
#include "util/time.h"
#include "xwayland.h"
static void handle_server_start(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat, server_start);
/* there is no hardware keyboard input device */
if (!seat->keyboard->wlr_keyboard->keymap) {
struct keymap_rules rules = { 0 };
struct xkb_keymap *keymap = input_get_or_create_keymap(&rules, true);
wlr_keyboard_set_keymap(seat->keyboard->wlr_keyboard, keymap);
}
wlr_seat_set_keyboard(seat->wlr_seat, seat->keyboard->wlr_keyboard);
if (seat->state.keyboard_lock & (1 << INPUT_KEY_CAPSLOCK)) {
seat_feed_keyboard_key(seat, KEY_CAPSLOCK, true);
seat_feed_keyboard_key(seat, KEY_CAPSLOCK, false);
}
if (seat->state.keyboard_lock & (1 << INPUT_KEY_NUMLOCK)) {
seat_feed_keyboard_key(seat, KEY_NUMLOCK, true);
seat_feed_keyboard_key(seat, KEY_NUMLOCK, false);
}
if (seat->state.keyboard_lock & (1 << INPUT_KEY_SCROLLLOCK)) {
seat_feed_keyboard_key(seat, KEY_SCROLLLOCK, true);
seat_feed_keyboard_key(seat, KEY_SCROLLLOCK, false);
}
}
static void handle_server_active(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat, server_active);
wlr_seat_set_keyboard(seat->wlr_seat, seat->keyboard->wlr_keyboard);
}
static void _seat_destroy(struct seat *seat)
{
kywc_log(KYWC_DEBUG, "Seat(%s) destroy", seat->name);
wl_signal_emit_mutable(&seat->events.destroy, NULL);
assert(wl_list_empty(&seat->events.cursor_motion.listener_list));
assert(wl_list_empty(&seat->events.cursor_configure.listener_list));
assert(wl_list_empty(&seat->events.keyboard_key.listener_list));
assert(wl_list_empty(&seat->events.keyboard_modifiers.listener_list));
assert(wl_list_empty(&seat->events.capability.listener_list));
assert(wl_list_empty(&seat->events.destroy.listener_list));
wl_list_remove(&seat->destroy.link);
wl_list_remove(&seat->link);
wl_list_remove(&seat->server_start.link);
wl_list_remove(&seat->server_active.link);
seat->state.keyboard_lock = keyboard_get_locks(seat->keyboard);
seat_write_config(seat);
/* cancel grab when seat destroy */
if (seat->pointer_grab && seat->pointer_grab->interface->cancel) {
seat->pointer_grab->interface->cancel(seat->pointer_grab);
}
if (seat->keyboard_grab && seat->keyboard_grab->interface->cancel) {
seat->keyboard_grab->interface->cancel(seat->keyboard_grab);
}
if (seat->touch_grab && seat->touch_grab->interface->cancel) {
seat->touch_grab->interface->cancel(seat->touch_grab);
}
struct input *input, *tmp;
wl_list_for_each_safe(input, tmp, &seat->inputs, seat_link) {
seat_remove_input(input);
}
struct keyboard *keyboard, *keyboard_tmp;
wl_list_for_each_safe(keyboard, keyboard_tmp, &seat->keyboards, link) {
keyboard_destroy(keyboard);
}
cursor_destroy(seat->cursor);
free((void *)seat->state.cursor_theme);
free(seat->name);
free(seat);
}
static void handle_seat_destroy(struct wl_listener *listener, void *data)
{
struct seat *seat = wl_container_of(listener, seat, destroy);
/* called when display destroy */
_seat_destroy(seat);
}
struct seat *seat_create(struct input_manager *input_manager, const char *name)
{
struct seat *seat = calloc(1, sizeof(*seat));
if (!seat) {
return NULL;
}
seat->wlr_seat = wlr_seat_create(input_manager->server->display, name);
if (!seat->wlr_seat) {
free(seat);
return NULL;
}
seat->wlr_seat->data = seat;
seat->scene = input_manager->server->scene;
seat->layout = input_manager->server->layout;
seat->pointer_gestures = input_manager->pointer_gestures;
seat->manager = input_manager;
seat->server_start.notify = handle_server_start;
wl_signal_add(&input_manager->server->events.start, &seat->server_start);
seat->server_active.notify = handle_server_active;
wl_signal_add(&input_manager->server->events.active, &seat->server_active);
seat->destroy.notify = handle_seat_destroy;
wl_signal_add(&seat->wlr_seat->events.destroy, &seat->destroy);
seat->name = strdup(name);
wl_list_init(&seat->inputs);
wl_list_init(&seat->keyboards);
wl_list_init(&seat->keyboard_shortcuts_inhibitors);
wl_signal_init(&seat->events.cursor_motion);
wl_signal_init(&seat->events.cursor_configure);
wl_signal_init(&seat->events.keyboard_key);
wl_signal_init(&seat->events.keyboard_modifiers);
wl_signal_init(&seat->events.capability);
wl_signal_init(&seat->events.destroy);
seat->state.cursor_theme = NULL;
seat->state.cursor_size = 24;
seat->state.keyboard_lock_mode = 2;
if (!seat_read_config(seat)) {
kywc_log(KYWC_ERROR, "Seat(%s) read config error!", seat->name);
}
seat->cursor = cursor_create(seat);
seat->keyboard = keyboard_create(seat, NULL);
cursor_set_hidden(seat->cursor, true);
wl_list_insert(&input_manager->seats, &seat->link);
kywc_log(KYWC_DEBUG, "Seat(%s) is created", seat->name);
wl_signal_emit_mutable(&input_manager->events.new_seat, seat);
return seat;
}
void seat_destroy(struct seat *seat)
{
struct wlr_seat *wlr_seat = seat->wlr_seat;
_seat_destroy(seat);
wlr_seat_destroy(wlr_seat);
}
static void seat_update_capabilities(struct seat *seat)
{
uint32_t old_caps = seat->caps;
seat->caps = 0;
struct input *input;
wl_list_for_each(input, &seat->inputs, seat_link) {
switch (input->prop.type) {
case WLR_INPUT_DEVICE_KEYBOARD:
seat->caps |= WL_SEAT_CAPABILITY_KEYBOARD;
break;
case WLR_INPUT_DEVICE_POINTER:
case WLR_INPUT_DEVICE_TABLET:
seat->caps |= WL_SEAT_CAPABILITY_POINTER;
break;
case WLR_INPUT_DEVICE_TOUCH:
seat->caps |= WL_SEAT_CAPABILITY_TOUCH;
break;
case WLR_INPUT_DEVICE_SWITCH:
case WLR_INPUT_DEVICE_TABLET_PAD:
break;
}
}
wlr_seat_set_capabilities(seat->wlr_seat, seat->caps);
/* auto hide cursor if no pointer cap */
cursor_set_hidden(seat->cursor, !(seat->caps & WL_SEAT_CAPABILITY_POINTER));
if (old_caps != seat->caps) {
wl_signal_emit_mutable(&seat->events.capability, NULL);
}
}
void seat_add_input(struct seat *seat, struct input *input)
{
input->seat = seat;
wl_list_insert(&seat->inputs, &input->seat_link);
switch (input->prop.type) {
case WLR_INPUT_DEVICE_POINTER:
case WLR_INPUT_DEVICE_TOUCH:
case WLR_INPUT_DEVICE_TABLET:
curosr_add_input(seat, input);
break;
case WLR_INPUT_DEVICE_KEYBOARD:
keyboard_add_input(seat, input);
break;
case WLR_INPUT_DEVICE_TABLET_PAD:
break;
case WLR_INPUT_DEVICE_SWITCH:
break;
}
seat_update_capabilities(seat);
}
void seat_remove_input(struct input *input)
{
struct seat *seat = input->seat;
if (!seat) {
return;
}
switch (input->prop.type) {
case WLR_INPUT_DEVICE_POINTER:
case WLR_INPUT_DEVICE_TOUCH:
case WLR_INPUT_DEVICE_TABLET:
cursor_remove_input(input);
break;
case WLR_INPUT_DEVICE_KEYBOARD:
keyboard_remove_input(input);
break;
case WLR_INPUT_DEVICE_TABLET_PAD:
break;
case WLR_INPUT_DEVICE_SWITCH:
break;
}
wl_list_remove(&input->seat_link);
input->seat = NULL;
seat_update_capabilities(seat);
}
struct seat *seat_from_resource(struct wl_resource *resource)
{
struct wlr_seat_client *seat_client = wl_resource_get_user_data(resource);
struct wlr_seat *wlr_seat = seat_client->seat;
return wlr_seat ? wlr_seat->data : NULL;
}
struct seat *seat_from_wlr_seat(struct wlr_seat *wlr_seat)
{
return wlr_seat->data;
}
void seat_set_cursor(struct seat *seat, const char *cursor_theme, uint32_t cursor_size)
{
cursor_set_xcursor_manager(seat->cursor, cursor_theme, cursor_size, true);
xwayland_set_cursor(seat);
seat_write_config(seat);
}
void seat_start_pointer_grab(struct seat *seat, struct seat_pointer_grab *pointer_grab)
{
struct seat_pointer_grab *grab = seat->pointer_grab;
if (grab == pointer_grab) {
return;
}
pointer_grab->seat = seat;
seat->pointer_grab = pointer_grab;
if (grab && grab->interface->cancel) {
grab->interface->cancel(grab);
}
/* when we move quickly with left button pressed at view edges,
* hold_mode is entered by a cursor motion event then client requests move
*/
seat->cursor->hold_mode = false;
}
void seat_end_pointer_grab(struct seat *seat, struct seat_pointer_grab *pointer_grab)
{
if (seat->pointer_grab == pointer_grab) {
seat->pointer_grab = NULL;
}
}
void seat_start_keyboard_grab(struct seat *seat, struct seat_keyboard_grab *keyboard_grab)
{
struct seat_keyboard_grab *grab = seat->keyboard_grab;
if (grab == keyboard_grab) {
return;
}
keyboard_grab->seat = seat;
seat->keyboard_grab = keyboard_grab;
if (grab && grab->interface->cancel) {
grab->interface->cancel(grab);
}
}
void seat_end_keyboard_grab(struct seat *seat, struct seat_keyboard_grab *keyboard_grab)
{
if (seat->keyboard_grab == keyboard_grab) {
seat->keyboard_grab = NULL;
}
}
void seat_start_touch_grab(struct seat *seat, struct seat_touch_grab *touch_grab)
{
struct seat_touch_grab *grab = seat->touch_grab;
if (grab == touch_grab) {
return;
}
touch_grab->seat = seat;
seat->touch_grab = touch_grab;
if (grab && grab->interface->cancel) {
grab->interface->cancel(grab);
}
}
void seat_end_touch_grab(struct seat *seat, struct seat_touch_grab *touch_grab)
{
if (seat->touch_grab == touch_grab) {
seat->touch_grab = NULL;
}
}
void seat_reset_input_gesture(struct seat *seat)
{
/* canceling gesture in the touch grab */
touch_reset_gesture(seat->manager);
}
void seat_notify_motion(struct seat *seat, struct wlr_surface *surface, uint32_t time, double sx,
double sy, bool first_enter)
{
struct wlr_seat *wlr_seat = seat->wlr_seat;
struct wlr_surface *prev = wlr_seat->pointer_state.focused_surface;
if (first_enter || surface != prev) {
wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
kywc_log(KYWC_DEBUG, "Pointer enter surface %p", surface);
}
wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
}
void seat_notify_button(struct seat *seat, uint32_t time, uint32_t button, bool pressed)
{
struct wlr_seat *wlr_seat = seat->wlr_seat;
enum wl_pointer_button_state state =
pressed ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED;
wlr_seat_pointer_notify_button(wlr_seat, time, button, state);
}
void seat_notify_leave(struct seat *seat, struct wlr_surface *surface)
{
struct wlr_seat *wlr_seat = seat->wlr_seat;
struct wlr_surface *prev = wlr_seat->pointer_state.focused_surface;
if (!surface || surface == prev) {
wlr_seat_pointer_notify_clear_focus(wlr_seat);
}
}
void seat_focus_surface(struct seat *seat, struct wlr_surface *surface)
{
struct wlr_seat *wlr_seat = seat->wlr_seat;
struct wlr_surface *last_surface = wlr_seat->keyboard_state.focused_surface;
if (last_surface != surface) {
struct seat_keyboard_shortcuts_inhibitor *shortcuts_inhibitor = NULL;
wl_list_for_each(shortcuts_inhibitor, &seat->keyboard_shortcuts_inhibitors, link) {
if (shortcuts_inhibitor->inhibitor->surface == last_surface) {
wlr_keyboard_shortcuts_inhibitor_v1_deactivate(shortcuts_inhibitor->inhibitor);
} else if (shortcuts_inhibitor->inhibitor->surface == surface) {
wlr_keyboard_shortcuts_inhibitor_v1_activate(shortcuts_inhibitor->inhibitor);
}
}
}
if (surface) {
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(wlr_seat);
if (keyboard) {
wlr_seat_keyboard_notify_enter(
wlr_seat, surface, seat->keyboard_grab ? NULL : keyboard->keycodes,
seat->keyboard_grab ? 0 : keyboard->num_keycodes, &keyboard->modifiers);
} else {
wlr_seat_keyboard_notify_enter(wlr_seat, surface, NULL, 0, NULL);
}
} else {
wlr_seat_keyboard_notify_clear_focus(wlr_seat);
}
tablet_set_focus(seat, surface);
input_method_set_focus(seat, surface);
cursor_constraint_set_focus(seat, surface);
}
void seat_feed_pointer_motion(struct seat *seat, double x, double y, bool absolute)
{
if (absolute) {
x = x - seat->cursor->lx;
y = y - seat->cursor->ly;
}
cursor_feed_motion(seat->cursor, current_time_msec(), NULL, x, y, x, y);
wlr_seat_pointer_notify_frame(seat->wlr_seat);
}
void seat_feed_pointer_button(struct seat *seat, uint32_t button, bool pressed)
{
cursor_feed_button(seat->cursor, button, pressed, current_time_msec(),
DEFAULT_DOUBLE_CLICK_TIME);
wlr_seat_pointer_notify_frame(seat->wlr_seat);
}
void seat_feed_pointer_axis(struct seat *seat, uint32_t axis, double step)
{
cursor_feed_axis(seat->cursor, axis, WL_POINTER_AXIS_SOURCE_WHEEL, step, 0, 0,
current_time_msec());
wlr_seat_pointer_notify_frame(seat->wlr_seat);
}
void seat_feed_keyboard_key(struct seat *seat, uint32_t key, bool pressed)
{
/* pick a keyboard to send key event */
struct keyboard *keyboard = seat->keyboard;
/* trying to find a keyboard that has input device */
if (keyboard_has_no_input(keyboard)) {
struct keyboard *kb;
wl_list_for_each(kb, &seat->keyboards, link) {
if (keyboard_has_no_input(kb)) {
continue;
}
keyboard = kb;
}
}
keyboard_send_key(keyboard, key, pressed);
}
bool seat_is_keyboard_shortcuts_inhibited(struct seat *seat)
{
struct wlr_surface *wlr_surface = seat->wlr_seat->keyboard_state.focused_surface;
struct seat_keyboard_shortcuts_inhibitor *shortcuts_inhibitor;
wl_list_for_each(shortcuts_inhibitor, &seat->keyboard_shortcuts_inhibitors, link) {
if (shortcuts_inhibitor->inhibitor->surface == wlr_surface) {
return shortcuts_inhibitor->inhibitor->active;
}
}
return false;
}
bool seat_is_dragging(struct seat *seat)
{
return selection_is_dragging(seat) || xwayland_is_dragging_x11(seat);
}
kylin-wayland-compositor/src/input/libinput.c 0000664 0001750 0001750 00000030403 15160460057 020403 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include "input_p.h"
void libinput_get_prop(struct input *input, struct input_prop *prop)
{
struct libinput_device *device = input->device;
if (!device) {
kywc_log(KYWC_DEBUG, "Input %s is not a libinput device", input->name);
return;
}
prop->send_events_modes = libinput_device_config_send_events_get_modes(device);
prop->click_methods = libinput_device_config_click_get_methods(device);
prop->scroll_methods = libinput_device_config_scroll_get_methods(device);
prop->accel_profiles = libinput_device_config_accel_get_profiles(device);
prop->tap_finger_count = libinput_device_config_tap_get_finger_count(device);
prop->has_calibration_matrix = libinput_device_config_calibration_has_matrix(device);
prop->has_pointer_accel = libinput_device_config_accel_is_available(device);
prop->has_natural_scroll = libinput_device_config_scroll_has_natural_scroll(device);
prop->has_left_handed = libinput_device_config_left_handed_is_available(device);
prop->has_middle_emulation = libinput_device_config_middle_emulation_is_available(device);
prop->has_dwt = libinput_device_config_dwt_is_available(device);
prop->has_dwtp = libinput_device_config_dwtp_is_available(device);
prop->has_rotation = libinput_device_config_rotation_is_available(device);
}
void libinput_get_state(struct input *input, struct input_state *state)
{
struct libinput_device *device = input->device;
if (!device) {
kywc_log(KYWC_DEBUG, "Input %s is not a libinput device", input->name);
return;
}
struct input_prop *prop = &input->prop;
state->send_events_mode = libinput_device_config_send_events_get_mode(device);
state->click_method = libinput_device_config_click_get_method(device);
if (prop->tap_finger_count > 0) {
state->tap_to_click = libinput_device_config_tap_get_enabled(device);
state->tap_button_map = libinput_device_config_tap_get_button_map(device);
state->tap_and_drag = libinput_device_config_tap_get_drag_enabled(device);
state->tap_drag_lock = libinput_device_config_tap_get_drag_lock_enabled(device);
}
if (prop->has_calibration_matrix) {
libinput_device_config_calibration_get_matrix(device, state->calibration_matrix);
}
if (prop->has_pointer_accel) {
state->pointer_accel_speed = libinput_device_config_accel_get_speed(device);
state->accel_profile = libinput_device_config_accel_get_profile(device);
}
if (prop->has_natural_scroll) {
state->natural_scroll = libinput_device_config_scroll_get_natural_scroll_enabled(device);
}
if (prop->has_left_handed) {
state->left_handed = libinput_device_config_left_handed_get(device);
}
if (prop->has_middle_emulation) {
state->middle_emulation = libinput_device_config_middle_emulation_get_enabled(device);
}
state->scroll_method = libinput_device_config_scroll_get_method(device);
if (state->scroll_method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) {
state->scroll_button = libinput_device_config_scroll_get_button(device);
state->scroll_button_lock = libinput_device_config_scroll_get_button_lock(device);
}
if (prop->has_dwt) {
state->dwt = libinput_device_config_dwt_get_enabled(device);
}
if (prop->has_dwtp) {
state->dwtp = libinput_device_config_dwtp_get_enabled(device);
}
if (prop->has_rotation) {
state->rotation_angle = libinput_device_config_rotation_get_angle(device);
}
}
void libinput_get_default_state(struct input *input, struct input_state *state)
{
struct libinput_device *device = input->device;
if (!device) {
kywc_log(KYWC_DEBUG, "Input %s is not a libinput device", input->name);
return;
}
struct input_prop *prop = &input->prop;
state->send_events_mode = libinput_device_config_send_events_get_default_mode(device);
state->click_method = libinput_device_config_click_get_default_method(device);
if (prop->tap_finger_count > 0) {
state->tap_to_click = libinput_device_config_tap_get_default_enabled(device);
state->tap_button_map = libinput_device_config_tap_get_default_button_map(device);
state->tap_and_drag = libinput_device_config_tap_get_default_drag_enabled(device);
state->tap_drag_lock = libinput_device_config_tap_get_default_drag_lock_enabled(device);
}
if (prop->has_calibration_matrix) {
libinput_device_config_calibration_get_default_matrix(device, state->calibration_matrix);
}
if (prop->has_pointer_accel) {
state->pointer_accel_speed = libinput_device_config_accel_get_default_speed(device);
state->accel_profile = libinput_device_config_accel_get_default_profile(device);
}
if (prop->has_natural_scroll) {
state->natural_scroll =
libinput_device_config_scroll_get_default_natural_scroll_enabled(device);
}
if (prop->has_left_handed) {
state->left_handed = libinput_device_config_left_handed_get_default(device);
}
if (prop->has_middle_emulation) {
state->middle_emulation =
libinput_device_config_middle_emulation_get_default_enabled(device);
}
state->scroll_method = libinput_device_config_scroll_get_default_method(device);
if (state->scroll_method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) {
state->scroll_button = libinput_device_config_scroll_get_default_button(device);
state->scroll_button_lock = libinput_device_config_scroll_get_default_button_lock(device);
}
if (prop->has_dwt) {
state->dwt = libinput_device_config_dwt_get_default_enabled(device);
}
if (prop->has_dwtp) {
state->dwtp = libinput_device_config_dwtp_get_default_enabled(device);
}
if (prop->has_rotation) {
state->rotation_angle = libinput_device_config_rotation_get_default_angle(device);
}
}
static bool log_status(enum libinput_config_status status)
{
if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) {
kywc_log(KYWC_ERROR, "Failed to apply libinput config: %s",
libinput_config_status_to_str(status));
return false;
}
return true;
}
bool libinput_set_state(struct input *input, struct input_state *state)
{
struct libinput_device *device = input->device;
if (!device) {
kywc_log(KYWC_DEBUG, "Input %s is not a libinput device", input->name);
return false;
}
struct input_state *current = &input->state;
bool success = true;
if (current->send_events_mode != state->send_events_mode) {
kywc_log(KYWC_DEBUG, "Do send_events_set_mode(%d)", state->send_events_mode);
success &= log_status(
libinput_device_config_send_events_set_mode(device, state->send_events_mode));
}
if (current->click_method != state->click_method) {
kywc_log(KYWC_DEBUG, "Do click_set_method(%d)", state->click_method);
success &= log_status(libinput_device_config_click_set_method(device, state->click_method));
}
if (input->prop.tap_finger_count > 0) {
if (current->tap_to_click != state->tap_to_click) {
kywc_log(KYWC_DEBUG, "Do tap_set_enabled(%d)", state->tap_to_click);
success &=
log_status(libinput_device_config_tap_set_enabled(device, state->tap_to_click));
}
// XXX: skip others ?
// if (!state->tap_to_click) {
// }
if (current->tap_and_drag != state->tap_and_drag) {
kywc_log(KYWC_DEBUG, "Do tap_set_drag_enabled(%d)", state->tap_and_drag);
success &= log_status(
libinput_device_config_tap_set_drag_enabled(device, state->tap_and_drag));
}
if (current->tap_drag_lock != state->tap_drag_lock) {
kywc_log(KYWC_DEBUG, "Do tap_set_drag_lock_enabled(%d)", state->tap_drag_lock);
success &= log_status(
libinput_device_config_tap_set_drag_lock_enabled(device, state->tap_drag_lock));
}
if (current->tap_button_map != state->tap_button_map) {
kywc_log(KYWC_DEBUG, "Do tap_set_button_map(%d)", state->tap_button_map);
success &= log_status(
libinput_device_config_tap_set_button_map(device, state->tap_button_map));
}
}
if (input->prop.has_calibration_matrix) {
bool changed = false;
for (int i = 0; i < 6; i++) {
if (current->calibration_matrix[i] != state->calibration_matrix[i]) {
changed = true;
break;
}
}
if (changed) {
kywc_log(KYWC_DEBUG, "Do calibration_set_matrix(%f, %f, %f, %f, %f, %f)",
state->calibration_matrix[0], state->calibration_matrix[1],
state->calibration_matrix[2], state->calibration_matrix[3],
state->calibration_matrix[4], state->calibration_matrix[5]);
success &= log_status(
libinput_device_config_calibration_set_matrix(device, state->calibration_matrix));
}
}
if (input->prop.has_pointer_accel) {
if (current->pointer_accel_speed != state->pointer_accel_speed) {
kywc_log(KYWC_DEBUG, "Do accel_set_speed(%f)", state->pointer_accel_speed);
success &= log_status(
libinput_device_config_accel_set_speed(device, state->pointer_accel_speed));
}
if (current->accel_profile != state->accel_profile) {
kywc_log(KYWC_DEBUG, "Do accel_set_profile(%d)", state->accel_profile);
success &=
log_status(libinput_device_config_accel_set_profile(device, state->accel_profile));
}
}
if (input->prop.has_natural_scroll && current->natural_scroll != state->natural_scroll) {
kywc_log(KYWC_DEBUG, "Do scroll_set_natural_scroll_enabled(%d)", state->natural_scroll);
success &= log_status(libinput_device_config_scroll_set_natural_scroll_enabled(
device, state->natural_scroll));
}
if (input->prop.has_left_handed && current->left_handed != state->left_handed) {
kywc_log(KYWC_DEBUG, "Do left_handed_set(%d)", state->left_handed);
success &= log_status(libinput_device_config_left_handed_set(device, state->left_handed));
}
if (input->prop.has_middle_emulation && current->middle_emulation != state->middle_emulation) {
kywc_log(KYWC_DEBUG, "Do middle_emulation_set_enabled(%d)", state->middle_emulation);
success &= log_status(
libinput_device_config_middle_emulation_set_enabled(device, state->middle_emulation));
}
if (current->scroll_method != state->scroll_method) {
kywc_log(KYWC_DEBUG, "Do scroll_set_method(%d)", state->scroll_method);
success &=
log_status(libinput_device_config_scroll_set_method(device, state->scroll_method));
}
if (state->scroll_method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) {
if (current->scroll_button != state->scroll_button) {
kywc_log(KYWC_DEBUG, "Do scroll_set_button(%d)", state->scroll_button);
success &=
log_status(libinput_device_config_scroll_set_button(device, state->scroll_button));
}
if (current->scroll_button_lock != state->scroll_button_lock) {
kywc_log(KYWC_DEBUG, "Do scroll_set_button_lock(%d)", state->scroll_button_lock);
success &= log_status(
libinput_device_config_scroll_set_button_lock(device, state->scroll_button_lock));
}
}
if (input->prop.has_dwt && current->dwt != state->dwt) {
kywc_log(KYWC_DEBUG, "Do dwt_set_enabled(%d)", state->dwt);
success &= log_status(libinput_device_config_dwt_set_enabled(device, state->dwt));
}
if (input->prop.has_dwtp && current->dwtp != state->dwtp) {
kywc_log(KYWC_DEBUG, "Do dwtp_set_enabled(%d)", state->dwtp);
success &= log_status(libinput_device_config_dwtp_set_enabled(device, state->dwtp));
}
if (input->prop.has_rotation && current->rotation_angle != state->rotation_angle) {
kywc_log(KYWC_DEBUG, "Do rotation_set_angle(%d)", state->rotation_angle);
success &=
log_status(libinput_device_config_rotation_set_angle(device, state->rotation_angle));
}
return success;
}
kylin-wayland-compositor/src/input/kde_keystate.c 0000664 0001750 0001750 00000024141 15160461067 021235 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include "input/seat.h"
#include "input_p.h"
#include "kde-keystate-protocol.h"
#include "server.h"
#define ORG_KDE_KWIN_KEYSTATE_VERSION 5
#define SETBIT(x, y) ((x) |= (1 << (y)))
#define CLRBIT(x, y) ((x) &= ~(1 << (y)))
#define REVBIT(x, y) ((x) ^= (1 << (y)))
#define GETBIT(x, y) (((x) >> (y)) & 1)
struct keystate_manager {
struct wl_global *global;
uint32_t keyboard_lock;
uint32_t release_record;
struct wl_list resources;
struct wl_list keyboards;
struct wl_listener new_seat;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct keystate_keyboard {
struct wl_list link;
uint32_t keycode;
struct wl_listener key;
struct wl_listener modifiers;
struct wl_listener destroy;
};
static struct keystate_manager *keystate_manager = NULL;
static void kde_keystate_fetchstates(struct wl_client *client, struct wl_resource *resource)
{
uint32_t state = 0;
state = GETBIT(keystate_manager->keyboard_lock, INPUT_KEY_CAPSLOCK)
? ORG_KDE_KWIN_KEYSTATE_STATE_LOCKED
: ORG_KDE_KWIN_KEYSTATE_STATE_UNLOCKED;
org_kde_kwin_keystate_send_stateChanged(resource, ORG_KDE_KWIN_KEYSTATE_KEY_CAPSLOCK, state);
state = GETBIT(keystate_manager->keyboard_lock, INPUT_KEY_NUMLOCK)
? ORG_KDE_KWIN_KEYSTATE_STATE_LOCKED
: ORG_KDE_KWIN_KEYSTATE_STATE_UNLOCKED;
org_kde_kwin_keystate_send_stateChanged(resource, ORG_KDE_KWIN_KEYSTATE_KEY_NUMLOCK, state);
state = GETBIT(keystate_manager->keyboard_lock, INPUT_KEY_SCROLLLOCK)
? ORG_KDE_KWIN_KEYSTATE_STATE_LOCKED
: ORG_KDE_KWIN_KEYSTATE_STATE_UNLOCKED;
org_kde_kwin_keystate_send_stateChanged(resource, ORG_KDE_KWIN_KEYSTATE_KEY_SCROLLLOCK, state);
uint32_t version = wl_resource_get_version(resource);
if (version < ORG_KDE_KWIN_KEYSTATE_VERSION) {
return;
}
struct seat *seat = input_manager_get_default_seat();
struct wlr_keyboard_modifiers *wlr_keyboard_modifiers =
&seat->keyboard->wlr_keyboard->modifiers;
if (wlr_keyboard_modifiers->depressed & WLR_MODIFIER_ALT) {
org_kde_kwin_keystate_send_stateChanged(resource, ORG_KDE_KWIN_KEYSTATE_KEY_ALT,
ORG_KDE_KWIN_KEYSTATE_STATE_PRESSED);
} else if (wlr_keyboard_modifiers->depressed & WLR_MODIFIER_CTRL) {
org_kde_kwin_keystate_send_stateChanged(resource, ORG_KDE_KWIN_KEYSTATE_KEY_CONTROL,
ORG_KDE_KWIN_KEYSTATE_STATE_PRESSED);
} else if (wlr_keyboard_modifiers->depressed & WLR_MODIFIER_SHIFT) {
org_kde_kwin_keystate_send_stateChanged(resource, ORG_KDE_KWIN_KEYSTATE_KEY_SHIFT,
ORG_KDE_KWIN_KEYSTATE_STATE_PRESSED);
} else if (wlr_keyboard_modifiers->depressed & WLR_MODIFIER_LOGO) {
org_kde_kwin_keystate_send_stateChanged(resource, ORG_KDE_KWIN_KEYSTATE_KEY_META,
ORG_KDE_KWIN_KEYSTATE_STATE_PRESSED);
}
}
static void kde_keystate_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct org_kde_kwin_keystate_interface kde_keystate_impl = {
.fetchStates = kde_keystate_fetchstates,
.destroy = kde_keystate_destroy,
};
static void kde_keystate_unbind(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
}
static void kde_keystate_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &org_kde_kwin_keystate_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &kde_keystate_impl, NULL, kde_keystate_unbind);
wl_list_insert(&keystate_manager->resources, wl_resource_get_link(resource));
}
static void keystate_keyboard_destroy(struct keystate_keyboard *keyboard)
{
wl_list_remove(&keyboard->destroy.link);
wl_list_remove(&keyboard->modifiers.link);
wl_list_remove(&keyboard->key.link);
wl_list_remove(&keyboard->link);
free(keyboard);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&keystate_manager->display_destroy.link);
wl_list_remove(&keystate_manager->new_seat.link);
if (keystate_manager->global) {
wl_global_destroy(keystate_manager->global);
}
struct keystate_keyboard *keyboard, *tmp;
wl_list_for_each_safe(keyboard, tmp, &keystate_manager->keyboards, link) {
keystate_keyboard_destroy(keyboard);
}
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&keystate_manager->server_destroy.link);
free(keystate_manager);
}
static void keyboard_lock_change(enum input_lock_key key)
{
uint32_t state = 0;
REVBIT(keystate_manager->keyboard_lock, key);
state = GETBIT(keystate_manager->keyboard_lock, key);
if (state) {
SETBIT(keystate_manager->release_record, key);
state = ORG_KDE_KWIN_KEYSTATE_STATE_LOCKED;
}
struct wl_resource *resource;
wl_resource_for_each(resource, &keystate_manager->resources) {
org_kde_kwin_keystate_send_stateChanged(resource, key, state);
}
}
static void keyboard_handle_lock(enum input_lock_key key, struct seat_keyboard_key_event *event)
{
if (GETBIT(keystate_manager->release_record, key)) {
CLRBIT(keystate_manager->release_record, key);
return;
}
if (GETBIT(keystate_manager->keyboard_lock, key) != event->pressed) {
keyboard_lock_change(key);
}
}
static void handle_key(struct wl_listener *listener, void *data)
{
struct keystate_keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct seat_keyboard_key_event *event = data;
keyboard->keycode = event->pressed ? event->keycode : 0;
switch (event->keycode) {
case KEY_CAPSLOCK:
keyboard_handle_lock(INPUT_KEY_CAPSLOCK, event);
break;
case KEY_NUMLOCK:
keyboard_handle_lock(INPUT_KEY_NUMLOCK, event);
break;
case KEY_SCROLLLOCK:
keyboard_handle_lock(INPUT_KEY_SCROLLLOCK, event);
break;
default:
return;
}
}
static void handle_modifiers(struct wl_listener *listener, void *data)
{
struct keystate_keyboard *keyboard = wl_container_of(listener, keyboard, modifiers);
struct wlr_keyboard_modifiers *modifiers = data;
uint32_t keycode = keyboard->keycode;
uint32_t key = 0;
switch (keycode) {
case KEY_LEFTSHIFT:
case KEY_RIGHTSHIFT:
if (modifiers->depressed & WLR_MODIFIER_SHIFT) {
key = ORG_KDE_KWIN_KEYSTATE_KEY_SHIFT;
}
break;
case KEY_LEFTCTRL:
case KEY_RIGHTCTRL:
if (modifiers->depressed & WLR_MODIFIER_CTRL) {
key = ORG_KDE_KWIN_KEYSTATE_KEY_CONTROL;
}
break;
case KEY_LEFTMETA:
case KEY_RIGHTMETA:
if (modifiers->depressed & WLR_MODIFIER_LOGO) {
key = ORG_KDE_KWIN_KEYSTATE_KEY_META;
}
break;
case KEY_LEFTALT:
if (modifiers->depressed & WLR_MODIFIER_ALT) {
key = ORG_KDE_KWIN_KEYSTATE_KEY_ALT;
}
break;
case KEY_RIGHTALT:
if (modifiers->depressed & WLR_MODIFIER_ALT) {
key = ORG_KDE_KWIN_KEYSTATE_KEY_ALTGR;
}
break;
default:
return;
}
if (!key) {
return;
}
struct wl_resource *resource;
wl_resource_for_each(resource, &keystate_manager->resources) {
uint32_t version = wl_resource_get_version(resource);
if (version >= ORG_KDE_KWIN_KEYSTATE_VERSION) {
org_kde_kwin_keystate_send_stateChanged(resource, key,
ORG_KDE_KWIN_KEYSTATE_STATE_PRESSED);
}
}
}
static void handle_destroy(struct wl_listener *listener, void *data)
{
struct keystate_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
keystate_keyboard_destroy(keyboard);
}
static void handle_new_seat(struct wl_listener *listener, void *data)
{
struct seat *seat = data;
struct keystate_keyboard *keystate_keyboard = calloc(1, sizeof(*keystate_keyboard));
if (!keystate_keyboard) {
return;
}
wl_list_insert(&keystate_manager->keyboards, &keystate_keyboard->link);
keystate_keyboard->key.notify = handle_key;
wl_signal_add(&seat->events.keyboard_key, &keystate_keyboard->key);
keystate_keyboard->modifiers.notify = handle_modifiers;
wl_signal_add(&seat->events.keyboard_modifiers, &keystate_keyboard->modifiers);
keystate_keyboard->destroy.notify = handle_destroy;
wl_signal_add(&seat->events.destroy, &keystate_keyboard->destroy);
}
bool kde_keystate_manager_create(struct input_manager *input_manager)
{
keystate_manager = calloc(1, sizeof(*keystate_manager));
if (!keystate_manager) {
return false;
}
wl_list_init(&keystate_manager->resources);
wl_list_init(&keystate_manager->keyboards);
keystate_manager->global =
wl_global_create(input_manager->server->display, &org_kde_kwin_keystate_interface,
ORG_KDE_KWIN_KEYSTATE_VERSION, keystate_manager, kde_keystate_bind);
if (!keystate_manager->global) {
kywc_log(KYWC_WARN, "Failed to create %s global", org_kde_kwin_keystate_interface.name);
free(keystate_manager);
return false;
}
keystate_manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(input_manager->server->display,
&keystate_manager->display_destroy);
keystate_manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(input_manager->server, &keystate_manager->server_destroy);
keystate_manager->new_seat.notify = handle_new_seat;
wl_signal_add(&input_manager->events.new_seat, &keystate_manager->new_seat);
return true;
}
kylin-wayland-compositor/src/input/event.c 0000664 0001750 0001750 00000004457 15160461067 017712 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include "input/event.h"
static void input_event_node_destroy(struct wl_listener *listener, void *data)
{
struct input_event_node *inode = wl_container_of(listener, inode, node_destroy);
wl_list_remove(&inode->node_destroy.link);
free(inode);
}
struct input_event_node *input_event_node_create(struct ky_scene_node *node,
const struct input_event_node_impl *impl,
input_event_node_get_root get_root,
input_event_node_get_toplevel get_toplevel,
void *data)
{
struct input_event_node *inode = calloc(1, sizeof(*inode));
if (!inode) {
return NULL;
}
inode->data = data;
inode->impl = impl;
inode->get_root = get_root;
inode->get_toplevel = get_toplevel;
node->data = inode;
inode->node_destroy.notify = input_event_node_destroy;
wl_signal_add(&node->events.destroy, &inode->node_destroy);
return inode;
}
static const struct input_event_node_impl dumb_impl = {
.click = NULL,
.hover = NULL,
.leave = NULL,
};
static struct ky_scene_node *dumb_get_root(void *data)
{
return data;
}
struct input_event_node *input_event_dumb_node_create(struct ky_scene_node *node,
struct ky_scene_node *root)
{
return input_event_node_create(node, &dumb_impl, dumb_get_root, NULL, root);
}
struct input_event_node *input_event_node_from_node(struct ky_scene_node *node)
{
struct ky_scene_node *n = node;
struct ky_scene_tree *parent;
while (n && !n->data) {
parent = n->parent;
n = parent ? &parent->node : NULL;
}
return n ? n->data : NULL;
}
struct ky_scene_node *input_event_node_root(struct input_event_node *event_node)
{
if (!event_node || !event_node->get_root) {
return NULL;
}
return event_node->get_root(event_node->data);
}
struct wlr_surface *input_event_node_toplevel(struct input_event_node *event_node)
{
if (!event_node || !event_node->get_toplevel) {
return NULL;
}
return event_node->get_toplevel(event_node->data);
}
kylin-wayland-compositor/src/input/tablet.c 0000664 0001750 0001750 00000054651 15160461067 020045 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include
#include
#include
#include "backend/libinput.h"
#include "input/seat.h"
#include "input_p.h"
#include "scene/surface.h"
#include "server.h"
#include "view/view.h"
#include "xwayland.h"
struct tablet_manager {
struct wlr_tablet_manager_v2 *manager;
struct wl_list tablets;
struct wl_list tablet_pads;
struct wl_listener new_input;
struct wl_listener server_destroy;
};
struct tablet {
struct wlr_tablet *wlr_tablet;
struct wlr_tablet_v2_tablet *tablet;
struct wl_list link;
struct input *input;
struct wl_listener input_destroy;
struct tablet_tool *tablet_tool;
};
struct tablet_tool {
struct wlr_tablet_v2_tablet_tool *tablet_tool;
struct tablet *tablet;
double tilt_x, tilt_y;
struct wl_listener set_cursor;
struct wl_listener tool_destroy;
struct wlr_surface *current_surface;
struct wl_listener surface_unmap;
};
struct tablet_pad {
struct wlr_tablet_pad *wlr_tablet_pad;
struct wlr_tablet_v2_tablet_pad *tablet_pad;
struct wl_list link;
struct tablet *tablet;
struct wl_listener tablet_destroy;
struct input *input;
struct wl_listener input_destroy;
struct wl_listener attach;
struct wl_listener button;
struct wl_listener ring;
struct wl_listener strip;
struct wlr_surface *current_surface;
struct wl_listener surface_destroy;
};
static struct tablet_manager *manager = NULL;
static struct tablet *tablet_from_wlr_tablet(struct wlr_tablet *wlr_tablet)
{
return wlr_tablet->data;
}
static struct tablet_tool *tablet_tool_from_wlr_tablet_tool(struct wlr_tablet_tool *wlr_tablet_tool)
{
return wlr_tablet_tool->data;
}
static void tablet_tool_handle_set_cursor(struct wl_listener *listener, void *data)
{
struct tablet_tool *tablet_tool = wl_container_of(listener, tablet_tool, set_cursor);
struct wlr_tablet_v2_event_cursor *event = data;
struct cursor *cursor = tablet_tool->tablet->input->seat->cursor;
if (cursor->seat->pointer_grab || cursor->hidden) {
return;
}
struct wl_client *focused_client = NULL;
struct wlr_surface *focused_surface = tablet_tool->tablet_tool->focused_surface;
if (focused_surface != NULL) {
focused_client = wl_resource_get_client(focused_surface->resource);
}
if (focused_client == NULL || event->seat_client->client != focused_client) {
kywc_log(KYWC_DEBUG, "Denying request to set cursor from unfocused client");
return;
}
cursor_set_surface(cursor, event->surface, event->hotspot_x, event->hotspot_y,
event->seat_client->client);
}
static void tablet_tool_handle_tool_destroy(struct wl_listener *listener, void *data)
{
struct tablet_tool *tablet_tool = wl_container_of(listener, tablet_tool, tool_destroy);
wl_list_remove(&tablet_tool->tool_destroy.link);
wl_list_remove(&tablet_tool->set_cursor.link);
wl_list_remove(&tablet_tool->surface_unmap.link);
tablet_tool->tablet->tablet_tool = NULL;
free(tablet_tool);
}
static void tablet_tool_handle_surface_unmap(struct wl_listener *listener, void *data)
{
struct tablet_tool *tablet_tool = wl_container_of(listener, tablet_tool, surface_unmap);
wl_list_remove(&tablet_tool->surface_unmap.link);
wl_list_init(&tablet_tool->surface_unmap.link);
tablet_tool->current_surface = NULL;
}
static struct tablet_tool *tablet_tool_create(struct tablet *tablet,
struct wlr_tablet_tool *wlr_tablet_tool)
{
struct tablet_tool *tablet_tool = calloc(1, sizeof(*tablet_tool));
if (!tablet_tool) {
return NULL;
}
tablet_tool->tablet = tablet;
wlr_tablet_tool->data = tablet_tool;
tablet->tablet_tool = tablet_tool;
wl_list_init(&tablet_tool->surface_unmap.link);
tablet_tool->surface_unmap.notify = tablet_tool_handle_surface_unmap;
tablet_tool->tablet_tool =
wlr_tablet_tool_create(manager->manager, tablet->input->seat->wlr_seat, wlr_tablet_tool);
tablet_tool->set_cursor.notify = tablet_tool_handle_set_cursor;
wl_signal_add(&tablet_tool->tablet_tool->events.set_cursor, &tablet_tool->set_cursor);
tablet_tool->tool_destroy.notify = tablet_tool_handle_tool_destroy;
wl_signal_add(&wlr_tablet_tool->events.destroy, &tablet_tool->tool_destroy);
return tablet_tool;
}
static void tablet_pad_handle_tablet_destroy(struct wl_listener *listener, void *data)
{
struct tablet_pad *tablet_pad = wl_container_of(listener, tablet_pad, tablet_destroy);
tablet_pad->tablet = NULL;
wl_list_remove(&tablet_pad->tablet_destroy.link);
wl_list_init(&tablet_pad->tablet_destroy.link);
}
static void attach_tablet_pad(struct tablet_pad *tablet_pad, struct tablet *tablet)
{
kywc_log(KYWC_DEBUG, "Attaching tablet pad \"%s\" to tablet tool \"%s\"",
tablet_pad->input->wlr_input->name, tablet->input->wlr_input->name);
tablet_pad->tablet = tablet;
wl_list_remove(&tablet_pad->tablet_destroy.link);
tablet_pad->tablet_destroy.notify = tablet_pad_handle_tablet_destroy;
wl_signal_add(&tablet->input->wlr_input->events.destroy, &tablet_pad->tablet_destroy);
}
static void tablet_handle_input_destroy(struct wl_listener *listener, void *data)
{
struct tablet *tablet = wl_container_of(listener, tablet, input_destroy);
wl_list_remove(&tablet->input_destroy.link);
wl_list_remove(&tablet->link);
free(tablet);
}
static void tablet_create(struct tablet_manager *manager, struct input *input)
{
struct tablet *tablet = calloc(1, sizeof(*tablet));
if (!tablet) {
return;
}
tablet->input = input;
tablet->input_destroy.notify = tablet_handle_input_destroy;
wl_signal_add(&input->events.destroy, &tablet->input_destroy);
wl_list_insert(&manager->tablets, &tablet->link);
tablet->wlr_tablet = wlr_tablet_from_input_device(input->wlr_input);
tablet->wlr_tablet->data = tablet;
tablet->wlr_tablet->usb_vendor_id = input->prop.vendor;
tablet->wlr_tablet->usb_product_id = input->prop.product;
tablet->tablet = wlr_tablet_create(manager->manager, input->seat->wlr_seat, input->wlr_input);
if (!input->device) {
return;
}
struct libinput_device_group *group =
libinput_device_get_device_group(wlr_libinput_get_device_handle(input->wlr_input));
struct tablet_pad *tablet_pad;
wl_list_for_each(tablet_pad, &manager->tablet_pads, link) {
if (!tablet_pad->input->device) {
continue;
}
struct libinput_device_group *tablet_pad_group = libinput_device_get_device_group(
wlr_libinput_get_device_handle(tablet_pad->input->wlr_input));
if (tablet_pad_group == group) {
attach_tablet_pad(tablet_pad, tablet);
break;
}
}
}
static void tablet_pad_handle_input_destroy(struct wl_listener *listener, void *data)
{
struct tablet_pad *tablet_pad = wl_container_of(listener, tablet_pad, input_destroy);
wl_list_remove(&tablet_pad->input_destroy.link);
wl_list_remove(&tablet_pad->link);
wl_list_remove(&tablet_pad->attach.link);
wl_list_remove(&tablet_pad->button.link);
wl_list_remove(&tablet_pad->strip.link);
wl_list_remove(&tablet_pad->ring.link);
wl_list_remove(&tablet_pad->tablet_destroy.link);
wl_list_remove(&tablet_pad->surface_destroy.link);
free(tablet_pad);
}
static void tablet_pad_handle_attach(struct wl_listener *listener, void *data)
{
struct tablet_pad *tablet_pad = wl_container_of(listener, tablet_pad, attach);
struct wlr_tablet_tool *wlr_tablet_tool = data;
struct tablet_tool *tablet_tool = tablet_tool_from_wlr_tablet_tool(wlr_tablet_tool);
if (!tablet_tool) {
return;
}
attach_tablet_pad(tablet_pad, tablet_tool->tablet);
}
static void tablet_pad_handle_button(struct wl_listener *listener, void *data)
{
struct tablet_pad *tablet_pad = wl_container_of(listener, tablet_pad, button);
struct wlr_tablet_pad_button_event *event = data;
if (input_event_filter(tablet_pad->input->seat, tablet_pad->input,
INPUT_EVENT_TABLET_PAD_BUTTON, event)) {
return;
}
if (!tablet_pad->current_surface) {
return;
}
wlr_tablet_v2_tablet_pad_notify_mode(tablet_pad->tablet_pad, event->group, event->mode,
event->time_msec);
wlr_tablet_v2_tablet_pad_notify_button(tablet_pad->tablet_pad, event->button, event->time_msec,
(enum zwp_tablet_pad_v2_button_state)event->state);
}
static void tablet_pad_handle_strip(struct wl_listener *listener, void *data)
{
struct tablet_pad *tablet_pad = wl_container_of(listener, tablet_pad, strip);
struct wlr_tablet_pad_strip_event *event = data;
if (input_event_filter(tablet_pad->input->seat, tablet_pad->input, INPUT_EVENT_TABLET_PAD_STRIP,
event)) {
return;
}
if (!tablet_pad->current_surface) {
return;
}
wlr_tablet_v2_tablet_pad_notify_strip(tablet_pad->tablet_pad, event->strip, event->position,
event->source == WLR_TABLET_PAD_STRIP_SOURCE_FINGER,
event->time_msec);
}
static void tablet_pad_handle_ring(struct wl_listener *listener, void *data)
{
struct tablet_pad *tablet_pad = wl_container_of(listener, tablet_pad, ring);
struct wlr_tablet_pad_ring_event *event = data;
if (input_event_filter(tablet_pad->input->seat, tablet_pad->input, INPUT_EVENT_TABLET_PAD_RING,
event)) {
return;
}
if (!tablet_pad->current_surface) {
return;
}
wlr_tablet_v2_tablet_pad_notify_ring(tablet_pad->tablet_pad, event->ring, event->position,
event->source == WLR_TABLET_PAD_RING_SOURCE_FINGER,
event->time_msec);
}
static void tablet_pad_create(struct tablet_manager *manager, struct input *input)
{
struct tablet_pad *tablet_pad = calloc(1, sizeof(*tablet_pad));
if (!tablet_pad) {
return;
}
tablet_pad->input = input;
tablet_pad->input_destroy.notify = tablet_pad_handle_input_destroy;
wl_signal_add(&input->events.destroy, &tablet_pad->input_destroy);
wl_list_insert(&manager->tablet_pads, &tablet_pad->link);
tablet_pad->wlr_tablet_pad = wlr_tablet_pad_from_input_device(input->wlr_input);
tablet_pad->wlr_tablet_pad->data = tablet_pad;
tablet_pad->tablet_pad =
wlr_tablet_pad_create(manager->manager, input->seat->wlr_seat, input->wlr_input);
tablet_pad->attach.notify = tablet_pad_handle_attach;
wl_signal_add(&tablet_pad->wlr_tablet_pad->events.attach_tablet, &tablet_pad->attach);
tablet_pad->button.notify = tablet_pad_handle_button;
wl_signal_add(&tablet_pad->wlr_tablet_pad->events.button, &tablet_pad->button);
tablet_pad->strip.notify = tablet_pad_handle_strip;
wl_signal_add(&tablet_pad->wlr_tablet_pad->events.strip, &tablet_pad->strip);
tablet_pad->ring.notify = tablet_pad_handle_ring;
wl_signal_add(&tablet_pad->wlr_tablet_pad->events.ring, &tablet_pad->ring);
wl_list_init(&tablet_pad->tablet_destroy.link);
wl_list_init(&tablet_pad->surface_destroy.link);
if (!input->device) {
return;
}
struct libinput_device_group *group =
libinput_device_get_device_group(wlr_libinput_get_device_handle(input->wlr_input));
struct tablet *tablet;
wl_list_for_each(tablet, &manager->tablets, link) {
if (!tablet->input->device) {
continue;
}
struct libinput_device_group *tablet_group = libinput_device_get_device_group(
wlr_libinput_get_device_handle(tablet->input->wlr_input));
if (tablet_group == group) {
attach_tablet_pad(tablet_pad, tablet);
break;
}
}
}
static void handle_new_input(struct wl_listener *listener, void *data)
{
struct input *input = data;
/* input has been configured, only care about tablet_tool and tablet_pad */
if (input->prop.type != WLR_INPUT_DEVICE_TABLET &&
input->prop.type != WLR_INPUT_DEVICE_TABLET_PAD) {
return;
}
if (input->prop.type == WLR_INPUT_DEVICE_TABLET) {
tablet_create(manager, input);
} else {
tablet_pad_create(manager, input);
}
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->server_destroy.link);
wl_list_remove(&manager->new_input.link);
free(manager);
manager = NULL;
}
bool tablet_manager_create(struct input_manager *input_manager)
{
manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->manager = wlr_tablet_v2_create(input_manager->server->display);
if (!manager->manager) {
kywc_log(KYWC_WARN, "Failed to create table manager");
free(manager);
manager = NULL;
return false;
}
wl_list_init(&manager->tablets);
wl_list_init(&manager->tablet_pads);
manager->new_input.notify = handle_new_input;
input_add_new_listener(&manager->new_input);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(input_manager->server, &manager->server_destroy);
return true;
}
static struct wlr_surface *tablet_get_surface(struct tablet *tablet, double *sx, double *sy,
struct wlr_surface **toplevel)
{
struct seat *seat = tablet->input->seat;
struct cursor *cursor = seat->cursor;
struct ky_scene_node *node =
ky_scene_node_at(&seat->scene->tree.node, cursor->lx, cursor->ly, sx, sy);
if (!node) {
return NULL;
}
if (toplevel) {
*toplevel = input_event_node_toplevel(input_event_node_from_node(node));
}
return wlr_surface_try_from_node(node);
}
static bool tablet_handle_tool_position(struct tablet_tool *tablet_tool)
{
struct cursor *cursor = tablet_tool->tablet->input->seat->cursor;
struct wlr_surface *current_surface = tablet_tool->current_surface;
double sx = 0, sy = 0;
struct wlr_surface *surface = tablet_get_surface(tablet_tool->tablet, &sx, &sy, NULL);
if (current_surface && surface != current_surface) {
int lx, ly;
struct ky_scene_buffer *scene_buffer = ky_scene_buffer_try_from_surface(current_surface);
ky_scene_node_coords(&scene_buffer->node, &lx, &ly);
sx = cursor->lx - lx;
sy = cursor->ly - ly;
} else if (surface && wlr_surface_accepts_tablet_v2(surface, tablet_tool->tablet->tablet)) {
wlr_tablet_v2_tablet_tool_notify_proximity_in(tablet_tool->tablet_tool,
tablet_tool->tablet->tablet, surface);
} else {
wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool->tablet_tool);
cursor_set_image(cursor, CURSOR_DEFAULT);
return false;
}
wlr_tablet_v2_tablet_tool_notify_motion(tablet_tool->tablet_tool, sx, sy);
selection_handle_cursor_move(cursor->seat, cursor->lx, cursor->ly);
return true;
}
bool tablet_handle_tool_proximity(struct wlr_tablet_tool_proximity_event *event)
{
struct tablet *tablet = tablet_from_wlr_tablet(event->tablet);
if (!tablet) {
return false;
}
/* create tablet_tool when proximity */
struct tablet_tool *tablet_tool = tablet_tool_from_wlr_tablet_tool(event->tool);
if (!tablet_tool) {
tablet_tool = tablet_tool_create(tablet, event->tool);
}
if (!tablet_tool) {
return false;
}
if (event->state == WLR_TABLET_TOOL_PROXIMITY_OUT) {
wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool->tablet_tool);
return true;
}
struct cursor *cursor = tablet_tool->tablet->input->seat->cursor;
cursor_move(cursor, &event->tablet->base, event->x, event->y, false, true);
return tablet_handle_tool_position(tablet_tool);
}
void tablet_handle_tool_axis(struct wlr_tablet_tool_axis_event *event)
{
struct tablet_tool *tablet_tool = tablet_tool_from_wlr_tablet_tool(event->tool);
if (!tablet_tool) {
return;
}
bool change_x = event->updated_axes & WLR_TABLET_TOOL_AXIS_X;
bool change_y = event->updated_axes & WLR_TABLET_TOOL_AXIS_Y;
if (change_x || change_y) {
struct cursor *cursor = tablet_tool->tablet->input->seat->cursor;
switch (event->tool->type) {
case WLR_TABLET_TOOL_TYPE_LENS:
case WLR_TABLET_TOOL_TYPE_MOUSE:
cursor_move(cursor, &event->tablet->base, event->dx, event->dy, true, false);
break;
default:
cursor_move(cursor, &event->tablet->base, change_x ? event->x : NAN,
change_y ? event->y : NAN, false, true);
break;
}
// cursor_feed_motion(cursor, event->time_msec, &event->tablet->base, 0, 0, 0, 0);
// wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
tablet_handle_tool_position(tablet_tool);
}
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE) {
wlr_tablet_v2_tablet_tool_notify_pressure(tablet_tool->tablet_tool, event->pressure);
}
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) {
wlr_tablet_v2_tablet_tool_notify_distance(tablet_tool->tablet_tool, event->distance);
}
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) {
tablet_tool->tilt_x = event->tilt_x;
}
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) {
tablet_tool->tilt_y = event->tilt_y;
}
if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) {
wlr_tablet_v2_tablet_tool_notify_tilt(tablet_tool->tablet_tool, tablet_tool->tilt_x,
tablet_tool->tilt_y);
}
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) {
wlr_tablet_v2_tablet_tool_notify_rotation(tablet_tool->tablet_tool, event->rotation);
}
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER) {
wlr_tablet_v2_tablet_tool_notify_slider(tablet_tool->tablet_tool, event->slider);
}
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL) {
wlr_tablet_v2_tablet_tool_notify_wheel(tablet_tool->tablet_tool, event->wheel_delta, 0);
}
}
bool tablet_handle_tool_tip(struct wlr_tablet_tool_tip_event *event)
{
struct tablet_tool *tablet_tool = tablet_tool_from_wlr_tablet_tool(event->tool);
if (!tablet_tool) {
return false;
}
struct wlr_surface *toplevel = NULL;
struct wlr_surface *surface = tablet_get_surface(tablet_tool->tablet, NULL, NULL, &toplevel);
wl_list_remove(&tablet_tool->surface_unmap.link);
wl_list_init(&tablet_tool->surface_unmap.link);
tablet_tool->current_surface = NULL;
if (!surface || !wlr_surface_accepts_tablet_v2(surface, tablet_tool->tablet->tablet)) {
if (event->state == WLR_TABLET_TOOL_TIP_UP) {
wlr_tablet_v2_tablet_tool_notify_up(tablet_tool->tablet_tool);
}
return false;
}
if (event->state == WLR_TABLET_TOOL_TIP_UP) {
wlr_tablet_v2_tablet_tool_notify_up(tablet_tool->tablet_tool);
return true;
}
tablet_tool->current_surface = surface;
wl_signal_add(&surface->events.unmap, &tablet_tool->surface_unmap);
wlr_tablet_v2_tablet_tool_notify_down(tablet_tool->tablet_tool);
wlr_tablet_tool_v2_start_implicit_grab(tablet_tool->tablet_tool);
/* activate and focus the toplevel surface */
if (toplevel) {
struct view *view = view_try_from_wlr_surface(toplevel);
if (view) {
kywc_view_activate(&view->base);
view_set_focus(view, tablet_tool->tablet->input->seat);
} else {
seat_focus_surface(tablet_tool->tablet->input->seat, toplevel);
}
}
return true;
}
bool tablet_handle_tool_button(struct wlr_tablet_tool_button_event *event)
{
struct tablet_tool *tablet_tool = tablet_tool_from_wlr_tablet_tool(event->tool);
if (!tablet_tool) {
return false;
}
struct wlr_surface *surface = tablet_get_surface(tablet_tool->tablet, NULL, NULL, NULL);
if (!surface || !wlr_surface_accepts_tablet_v2(surface, tablet_tool->tablet->tablet)) {
return false;
}
wlr_tablet_v2_tablet_tool_notify_button(tablet_tool->tablet_tool, event->button,
(enum zwp_tablet_pad_v2_button_state)event->state);
return true;
}
static void tablet_pad_set_focus(struct tablet_pad *tablet_pad, struct wlr_surface *surface);
static void tablet_pad_handle_surface_destroy(struct wl_listener *listener, void *data)
{
struct tablet_pad *tablet_pad = wl_container_of(listener, tablet_pad, surface_destroy);
tablet_pad_set_focus(tablet_pad, NULL);
}
static void tablet_pad_set_focus(struct tablet_pad *tablet_pad, struct wlr_surface *surface)
{
if (!tablet_pad || !tablet_pad->tablet) {
return;
}
if (surface == tablet_pad->current_surface) {
return;
}
/* Leave current surface */
if (tablet_pad->current_surface) {
wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad->tablet_pad, tablet_pad->current_surface);
wl_list_remove(&tablet_pad->surface_destroy.link);
wl_list_init(&tablet_pad->surface_destroy.link);
tablet_pad->current_surface = NULL;
}
if (surface == NULL || !wlr_surface_accepts_tablet_v2(surface, tablet_pad->tablet->tablet)) {
return;
}
wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad->tablet_pad, tablet_pad->tablet->tablet,
surface);
tablet_pad->current_surface = surface;
tablet_pad->surface_destroy.notify = tablet_pad_handle_surface_destroy;
wl_signal_add(&surface->events.destroy, &tablet_pad->surface_destroy);
}
void tablet_set_focus(struct seat *seat, struct wlr_surface *surface)
{
if (!manager) {
return;
}
struct tablet_pad *tablet_pad;
wl_list_for_each(tablet_pad, &manager->tablet_pads, link) {
tablet_pad_set_focus(tablet_pad, surface);
}
}
bool tablet_has_implicit_grab(struct seat *seat)
{
struct tablet *tablet;
wl_list_for_each(tablet, &manager->tablets, link) {
if (tablet->input->seat == seat && tablet->tablet_tool &&
wlr_tablet_tool_v2_has_implicit_grab(tablet->tablet_tool->tablet_tool)) {
return true;
}
}
return false;
}
kylin-wayland-compositor/src/input/monitor.c 0000664 0001750 0001750 00000015401 15160461067 020247 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include "input_p.h"
#include "output.h"
#include "server.h"
struct cursor_output {
struct wl_list link;
struct input_monitor *monitor;
struct kywc_output *output;
struct wl_listener on;
struct wl_listener destroy;
};
struct input_monitor {
struct input_manager *input_manager;
struct wl_list outputs;
struct wl_listener new_output;
struct wl_listener configured;
struct wl_listener layout_damage;
struct wl_listener new_seat;
struct wl_listener server_destroy;
};
static void seat_rebase_cursor(struct seat *seat, bool initial)
{
// lx/ly might change when pick output
double lx = seat->cursor->lx;
double ly = seat->cursor->ly;
struct output *output = input_current_output(seat);
if (output && !output->base.destroying &&
(!seat->manager->server->start || initial ||
!kywc_output_contains_point(&output->base, lx, ly))) {
struct kywc_box geo = output->geometry;
geo.x += geo.width / 2;
geo.y += geo.height / 2;
cursor_move(seat->cursor, NULL, geo.x, geo.y, false, false);
}
cursor_rebase(seat->cursor);
}
static void handle_output_destroy(struct wl_listener *listener, void *data)
{
struct cursor_output *cursor_output = wl_container_of(listener, cursor_output, destroy);
wl_list_remove(&cursor_output->link);
wl_list_remove(&cursor_output->on.link);
wl_list_remove(&cursor_output->destroy.link);
free(cursor_output);
}
static void handle_output_on(struct wl_listener *listener, void *data)
{
struct cursor_output *cursor_output = wl_container_of(listener, cursor_output, on);
struct kywc_output *kywc_output = cursor_output->output;
if (kywc_output != kywc_output_get_primary()) {
return;
}
struct input *input;
struct input_manager *input_manager = cursor_output->monitor->input_manager;
wl_list_for_each(input, &input_manager->inputs, link) {
if (input->prop.type != WLR_INPUT_DEVICE_TOUCH || input->mapped_output) {
continue;
}
// mapped to primary output
struct input_state state = input->state;
state.mapped_to_output = kywc_output->name;
input_set_state(input, &state);
}
}
static void handle_new_output(struct wl_listener *listener, void *data)
{
struct cursor_output *cursor_output = calloc(1, sizeof(*cursor_output));
if (!cursor_output) {
return;
}
struct input_monitor *input_monitor = wl_container_of(listener, input_monitor, new_output);
struct kywc_output *kywc_output = data;
cursor_output->monitor = input_monitor;
cursor_output->output = kywc_output;
wl_list_insert(&input_monitor->outputs, &cursor_output->link);
cursor_output->on.notify = handle_output_on;
wl_signal_add(&kywc_output->events.on, &cursor_output->on);
cursor_output->destroy.notify = handle_output_destroy;
wl_signal_add(&kywc_output->events.destroy, &cursor_output->destroy);
if (kywc_output->state.enabled) {
handle_output_on(&cursor_output->on, NULL);
}
}
static void handle_configured(struct wl_listener *listener, void *data)
{
struct configure_event *event = data;
if (event->type == CONFIGURE_TYPE_NONE) {
return;
}
struct input_monitor *input_monitor = wl_container_of(listener, input_monitor, configured);
struct seat *seat;
wl_list_for_each(seat, &input_monitor->input_manager->seats, link) {
seat_rebase_cursor(seat, event->type == CONFIGURE_TYPE_INIT);
}
}
static void handle_layout_damage(struct wl_listener *listener, void *data)
{
struct input_monitor *input_monitor = wl_container_of(listener, input_monitor, layout_damage);
pixman_region32_t *damage_region = data;
struct seat *seat;
wl_list_for_each(seat, &input_monitor->input_manager->seats, link) {
/* skip motion when has grab */
if (seat->pointer_grab && seat->pointer_grab->interface->motion) {
continue;
}
if (seat_is_dragging(seat) || seat->cursor->hold_mode) {
continue;
}
double lx = seat->cursor->lx;
double ly = seat->cursor->ly;
if (!pixman_region32_contains_point(damage_region, lx, ly, NULL)) {
continue;
}
// check node in cursor position
struct ky_scene_node *node = ky_scene_node_at(&seat->scene->tree.node, lx, ly, NULL, NULL);
if (node != seat->cursor->hover.node) {
cursor_rebase(seat->cursor);
}
}
}
static void handle_seat_idle(struct idle *idle, void *data) {};
static void handle_seat_resume(struct idle *idle, void *data)
{
output_manager_power_outputs(true);
}
static void handle_new_seat(struct wl_listener *listener, void *data)
{
struct input_monitor *input_monitor = wl_container_of(listener, input_monitor, new_seat);
struct seat *seat = data;
seat_rebase_cursor(seat, true);
idle_manager_add_idle(seat, false, 0, handle_seat_idle, handle_seat_resume, NULL, NULL);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct input_monitor *input_monitor = wl_container_of(listener, input_monitor, server_destroy);
wl_list_remove(&input_monitor->server_destroy.link);
wl_list_remove(&input_monitor->new_seat.link);
wl_list_remove(&input_monitor->new_output.link);
wl_list_remove(&input_monitor->configured.link);
wl_list_remove(&input_monitor->layout_damage.link);
struct cursor_output *cursor_output, *tmp;
wl_list_for_each_safe(cursor_output, tmp, &input_monitor->outputs, link) {
handle_output_destroy(&cursor_output->destroy, NULL);
}
free(input_monitor);
}
struct input_monitor *input_monitor_create(struct input_manager *input_manager)
{
struct input_monitor *input_monitor = calloc(1, sizeof(*input_monitor));
if (!input_monitor) {
return NULL;
}
wl_list_init(&input_monitor->outputs);
input_monitor->input_manager = input_manager;
input_monitor->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(input_manager->server, &input_monitor->server_destroy);
input_monitor->new_output.notify = handle_new_output;
kywc_output_add_new_listener(&input_monitor->new_output);
input_monitor->configured.notify = handle_configured;
output_manager_add_configured_listener(&input_monitor->configured);
input_monitor->layout_damage.notify = handle_layout_damage;
// output_manager_add_layout_damage_listener(&input_monitor->layout_damage);
wl_list_init(&input_monitor->layout_damage.link);
input_monitor->new_seat.notify = handle_new_seat;
wl_signal_add(&input_manager->events.new_seat, &input_monitor->new_seat);
return input_monitor;
}
kylin-wayland-compositor/src/input/text_input.c 0000664 0001750 0001750 00000113177 15160461067 020774 0 ustar feng feng // SPDX-FileCopyrightText: 2016-2017 Drew DeVault
// SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include
#include
#include
#include "input/event.h"
#include "input_p.h"
#include "output.h"
#include "scene/surface.h"
#include "server.h"
#include "text_input_v1.h"
#include "text_input_v2.h"
#include "view/view.h"
/* most codes are copied from sway input/text_input.c */
struct input_method_manager {
struct wlr_input_method_manager_v2 *input_method;
struct text_input_manager_v1 *text_input_v1;
struct wl_listener new_text_input_v1;
struct wl_listener text_input_v1_destroy;
struct text_input_manager_v2 *text_input_v2;
struct wlr_text_input_manager_v3 *text_input_v3;
struct wl_listener new_seat;
struct wl_listener server_destroy;
};
/* input method per seat */
struct input_method_relay {
struct seat *seat;
struct wl_listener seat_destroy;
struct wl_list text_inputs;
struct wl_listener new_text_input_v2;
struct wl_listener text_input_v2_destroy;
struct wl_listener new_text_input_v3;
struct wl_listener text_input_v3_destroy;
struct wlr_input_method_v2 *wlr_input_method;
struct wl_listener input_method_v2_destroy;
struct wl_listener new_input_method;
struct wl_listener input_method_commit;
struct wl_listener input_method_grab_keyboard;
struct wl_listener input_method_keyboard_grab_destroy;
struct wl_listener input_method_destroy;
struct wl_list input_popups;
struct wl_listener new_popup_surface;
struct text_input *pending_enabled_text_input;
};
struct text_input {
struct text_input_v1 *text_input_v1;
struct text_input_v2 *text_input_v2;
struct wlr_text_input_v3 *text_input_v3;
struct input_method_relay *relay;
struct wl_list link;
struct wl_listener text_input_enable;
struct wl_listener text_input_commit;
struct wl_listener text_input_disable;
struct wl_listener text_input_destroy;
struct wlr_surface *pending_focused_surface;
struct wl_listener pending_focused_surface_destroy;
};
struct input_popup {
struct wlr_input_popup_surface_v2 *popup_surface;
struct input_method_relay *relay;
struct wl_list link;
struct ky_scene_node *surface_node;
/* not map/unmap handle in scene surface */
struct wl_listener surface_map;
struct wl_listener surface_commit;
struct wl_listener surface_unmap;
struct wl_listener popup_surface_destroy;
};
static struct wlr_surface *text_input_focused_surface(struct text_input *text_input)
{
if (text_input->text_input_v1) {
return text_input->text_input_v1->surface;
} else if (text_input->text_input_v2) {
return text_input->text_input_v2->surface;
} else {
return text_input->text_input_v3->focused_surface;
}
}
static void text_input_send_leave(struct text_input *text_input)
{
if (text_input->text_input_v3) {
wlr_text_input_v3_send_leave(text_input->text_input_v3);
} else if (text_input->text_input_v2) {
text_input_v2_send_leave(text_input->text_input_v2);
} else {
text_input_v1_send_leave(text_input->text_input_v1);
}
}
static void text_input_send_enter(struct text_input *text_input, struct wlr_surface *surface)
{
if (text_input->text_input_v3) {
wlr_text_input_v3_send_enter(text_input->text_input_v3, surface);
} else if (text_input->text_input_v2) {
text_input_v2_send_enter(text_input->text_input_v2, surface);
} else {
text_input_v1_send_enter(text_input->text_input_v1, surface);
}
}
static struct text_input *relay_get_focused_text_input(struct input_method_relay *relay)
{
struct text_input *text_input = NULL;
wl_list_for_each(text_input, &relay->text_inputs, link) {
if (text_input_focused_surface(text_input)) {
return text_input;
}
}
return NULL;
}
static void input_popup_update(struct input_popup *popup, struct seat *seat)
{
if (!popup->popup_surface->surface->mapped) {
return;
}
struct text_input *text_input = relay_get_focused_text_input(popup->relay);
if (!text_input) {
return;
}
struct wlr_surface *focused_surface = text_input_focused_surface(text_input);
/* workaround: view_destroy is emitted before surface_destroy */
if (!focused_surface || !focused_surface->mapped) {
return;
}
struct ky_scene_buffer *scene_buffer = ky_scene_buffer_try_from_surface(focused_surface);
assert(scene_buffer);
/* surface primary output */
if (!scene_buffer->primary_output) {
return;
}
struct kywc_box *output_box =
&output_from_wlr_output(scene_buffer->primary_output->output)->geometry;
/* focused surface geometry in layout coord */
struct kywc_box parent_box;
ky_scene_node_coords(&scene_buffer->node, &parent_box.x, &parent_box.y);
parent_box.width = focused_surface->current.width;
parent_box.height = focused_surface->current.height;
struct wlr_box cursor_box = { 0 };
if (text_input->text_input_v3) {
cursor_box = text_input->text_input_v3->current.cursor_rectangle;
} else if (text_input->text_input_v2) {
cursor_box = text_input->text_input_v2->cursor_rectangle;
} else {
cursor_box = text_input->text_input_v1->cursor_rectangle;
}
int surface_width = popup->popup_surface->surface->current.width;
int surface_height = popup->popup_surface->surface->current.height;
/* constraints for output */
int dx = cursor_box.x + parent_box.x + surface_width - output_box->x - output_box->width;
int dy = cursor_box.y + cursor_box.height + parent_box.y + surface_height - output_box->y -
output_box->height;
if (dx > 0) {
cursor_box.x -= dx;
}
if (dy > 0) {
cursor_box.y -= cursor_box.height + surface_height;
}
wlr_input_popup_surface_v2_send_text_input_rectangle(popup->popup_surface, &cursor_box);
int x = cursor_box.x + parent_box.x;
int y = cursor_box.y + cursor_box.height + parent_box.y;
ky_scene_node_set_position(popup->surface_node, x, y);
}
static void relay_send_input_method_state(struct input_method_relay *relay,
struct text_input *text_input)
{
struct wlr_input_method_v2 *input_method = relay->wlr_input_method;
// TODO: only send each of those if they were modified
if (text_input->text_input_v3) {
if (text_input->text_input_v3->active_features &
WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) {
wlr_input_method_v2_send_surrounding_text(
input_method, text_input->text_input_v3->current.surrounding.text,
text_input->text_input_v3->current.surrounding.cursor,
text_input->text_input_v3->current.surrounding.anchor);
}
wlr_input_method_v2_send_text_change_cause(
input_method, text_input->text_input_v3->current.text_change_cause);
if (text_input->text_input_v3->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE) {
wlr_input_method_v2_send_content_type(
input_method, text_input->text_input_v3->current.content_type.hint,
text_input->text_input_v3->current.content_type.purpose);
}
} else if (text_input->text_input_v2) {
if (text_input->text_input_v2->surrounding.pending) {
wlr_input_method_v2_send_surrounding_text(
input_method, text_input->text_input_v2->surrounding.text,
text_input->text_input_v2->surrounding.cursor,
text_input->text_input_v2->surrounding.anchor);
}
wlr_input_method_v2_send_text_change_cause(input_method, 0);
if (text_input->text_input_v2->content_type.pending) {
wlr_input_method_v2_send_content_type(input_method,
text_input->text_input_v2->content_type.hint,
text_input->text_input_v2->content_type.purpose);
}
} else {
if (text_input->text_input_v1->surrounding.pending) {
wlr_input_method_v2_send_surrounding_text(
input_method, text_input->text_input_v1->surrounding.text,
text_input->text_input_v1->surrounding.cursor,
text_input->text_input_v1->surrounding.anchor);
}
wlr_input_method_v2_send_text_change_cause(input_method, 0);
if (text_input->text_input_v1->content_type.pending) {
wlr_input_method_v2_send_content_type(input_method,
text_input->text_input_v1->content_type.hint,
text_input->text_input_v1->content_type.purpose);
}
}
struct input_popup *popup;
wl_list_for_each(popup, &relay->input_popups, link) {
input_popup_update(popup, relay->seat);
}
wlr_input_method_v2_send_done(input_method);
}
static void handle_text_input_enable(struct wl_listener *listener, void *data)
{
struct text_input *text_input = wl_container_of(listener, text_input, text_input_enable);
#if 0
if (text_input->text_input_v1) {
assert(!text_input->relay);
assert(text_input->text_input_v1->seat);
text_input->relay = seat_from_wlr_seat(text_input->text_input_v1->seat)->relay;
wl_list_insert(&text_input->relay->text_inputs, &text_input->link);
}
#endif
if (text_input->relay->wlr_input_method == NULL) {
text_input->relay->pending_enabled_text_input = text_input;
kywc_log(KYWC_INFO, "Enabling text input when input method is gone");
return;
}
wlr_input_method_v2_send_activate(text_input->relay->wlr_input_method);
relay_send_input_method_state(text_input->relay, text_input);
}
static bool text_input_is_enabeld(struct text_input *text_input)
{
if (text_input->text_input_v3) {
return text_input->text_input_v3->current_enabled;
} else if (text_input->text_input_v2) {
return text_input->text_input_v2->enabled;
} else {
return text_input->text_input_v1->activated;
}
}
static void handle_text_input_commit(struct wl_listener *listener, void *data)
{
struct text_input *text_input = wl_container_of(listener, text_input, text_input_commit);
if (!text_input_is_enabeld(text_input)) {
kywc_log(KYWC_DEBUG, "Inactive text input tried to commit an update");
return;
}
// kywc_log(KYWC_DEBUG, "Text input committed update");
if (text_input->relay->wlr_input_method == NULL) {
kywc_log(KYWC_INFO, "Text input committed, but input method is gone");
return;
}
relay_send_input_method_state(text_input->relay, text_input);
}
static void relay_disable_text_input(struct input_method_relay *relay,
struct text_input *text_input)
{
if (relay->wlr_input_method == NULL) {
kywc_log(KYWC_DEBUG, "Disabling text input, but input method is gone");
return;
}
wlr_input_method_v2_send_deactivate(relay->wlr_input_method);
relay_send_input_method_state(relay, text_input);
#if 0
/* clear seat relay and updated when enable */
if (text_input->text_input_v1) {
wl_list_remove(&text_input->link);
wl_list_init(&text_input->link);
text_input->relay = NULL;
}
#endif
}
static void handle_text_input_disable(struct wl_listener *listener, void *data)
{
struct text_input *text_input = wl_container_of(listener, text_input, text_input_disable);
if (text_input->relay->pending_enabled_text_input == text_input) {
text_input->relay->pending_enabled_text_input = NULL;
}
if (!text_input_focused_surface(text_input)) {
kywc_log(KYWC_DEBUG, "Disabling text input, but no longer focused");
return;
}
relay_disable_text_input(text_input->relay, text_input);
}
static void text_input_set_pending_focused_surface(struct text_input *text_input,
struct wlr_surface *surface)
{
wl_list_remove(&text_input->pending_focused_surface_destroy.link);
text_input->pending_focused_surface = surface;
if (surface) {
wl_signal_add(&surface->events.destroy, &text_input->pending_focused_surface_destroy);
} else {
wl_list_init(&text_input->pending_focused_surface_destroy.link);
}
}
static void handle_text_input_destroy(struct wl_listener *listener, void *data)
{
struct text_input *text_input = wl_container_of(listener, text_input, text_input_destroy);
if (text_input_is_enabeld(text_input)) {
relay_disable_text_input(text_input->relay, text_input);
}
text_input_set_pending_focused_surface(text_input, NULL);
if (text_input->relay->pending_enabled_text_input == text_input) {
text_input->relay->pending_enabled_text_input = NULL;
}
wl_list_remove(&text_input->link);
wl_list_remove(&text_input->text_input_commit.link);
wl_list_remove(&text_input->text_input_destroy.link);
wl_list_remove(&text_input->text_input_disable.link);
wl_list_remove(&text_input->text_input_enable.link);
free(text_input);
}
static void handle_pending_focused_surface_destroy(struct wl_listener *listener, void *data)
{
struct text_input *text_input =
wl_container_of(listener, text_input, pending_focused_surface_destroy);
struct wlr_surface *surface = data;
assert(text_input->pending_focused_surface == surface);
text_input->pending_focused_surface = NULL;
wl_list_remove(&text_input->pending_focused_surface_destroy.link);
wl_list_init(&text_input->pending_focused_surface_destroy.link);
}
static void handle_new_text_input_v3(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, new_text_input_v3);
struct wlr_text_input_v3 *wlr_text_input = data;
if (relay->seat->wlr_seat != wlr_text_input->seat) {
return;
}
struct text_input *text_input = calloc(1, sizeof(*text_input));
if (!text_input) {
return;
}
text_input->text_input_v3 = wlr_text_input;
text_input->relay = relay;
wl_list_insert(&relay->text_inputs, &text_input->link);
text_input->text_input_enable.notify = handle_text_input_enable;
wl_signal_add(&wlr_text_input->events.enable, &text_input->text_input_enable);
text_input->text_input_commit.notify = handle_text_input_commit;
wl_signal_add(&wlr_text_input->events.commit, &text_input->text_input_commit);
text_input->text_input_disable.notify = handle_text_input_disable;
wl_signal_add(&wlr_text_input->events.disable, &text_input->text_input_disable);
text_input->text_input_destroy.notify = handle_text_input_destroy;
wl_signal_add(&wlr_text_input->events.destroy, &text_input->text_input_destroy);
text_input->pending_focused_surface_destroy.notify = handle_pending_focused_surface_destroy;
wl_list_init(&text_input->pending_focused_surface_destroy.link);
}
static void handle_new_text_input_v1(struct wl_listener *listener, void *data)
{
struct text_input *text_input = calloc(1, sizeof(*text_input));
if (!text_input) {
return;
}
struct text_input_v1 *text_input_v1 = data;
text_input->text_input_v1 = text_input_v1;
/* no seat set */
// wl_list_init(&text_input->link);
// TODO: multi-seat not support
text_input->relay = input_manager_get_default_seat()->relay;
wl_list_insert(&text_input->relay->text_inputs, &text_input->link);
text_input->text_input_enable.notify = handle_text_input_enable;
wl_signal_add(&text_input_v1->events.activate, &text_input->text_input_enable);
text_input->text_input_commit.notify = handle_text_input_commit;
wl_signal_add(&text_input_v1->events.commit, &text_input->text_input_commit);
text_input->text_input_disable.notify = handle_text_input_disable;
wl_signal_add(&text_input_v1->events.deactivate, &text_input->text_input_disable);
text_input->text_input_destroy.notify = handle_text_input_destroy;
wl_signal_add(&text_input_v1->events.destroy, &text_input->text_input_destroy);
text_input->pending_focused_surface_destroy.notify = handle_pending_focused_surface_destroy;
wl_list_init(&text_input->pending_focused_surface_destroy.link);
}
static void handle_new_text_input_v2(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, new_text_input_v2);
struct text_input_v2 *text_input_v2 = data;
if (relay->seat->wlr_seat != text_input_v2->seat) {
return;
}
struct text_input *text_input = calloc(1, sizeof(*text_input));
if (!text_input) {
return;
}
text_input->text_input_v2 = text_input_v2;
text_input->relay = relay;
wl_list_insert(&relay->text_inputs, &text_input->link);
text_input->text_input_enable.notify = handle_text_input_enable;
wl_signal_add(&text_input_v2->events.enable, &text_input->text_input_enable);
text_input->text_input_commit.notify = handle_text_input_commit;
wl_signal_add(&text_input_v2->events.commit, &text_input->text_input_commit);
text_input->text_input_disable.notify = handle_text_input_disable;
wl_signal_add(&text_input_v2->events.disable, &text_input->text_input_disable);
text_input->text_input_destroy.notify = handle_text_input_destroy;
wl_signal_add(&text_input_v2->events.destroy, &text_input->text_input_destroy);
text_input->pending_focused_surface_destroy.notify = handle_pending_focused_surface_destroy;
wl_list_init(&text_input->pending_focused_surface_destroy.link);
}
static void handle_input_method_commit(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, input_method_commit);
struct text_input *text_input = relay_get_focused_text_input(relay);
if (!text_input) {
return;
}
struct wlr_input_method_v2 *context = data;
assert(context == relay->wlr_input_method);
if (text_input->text_input_v3) {
if (context->current.preedit.text) {
wlr_text_input_v3_send_preedit_string(
text_input->text_input_v3, context->current.preedit.text,
context->current.preedit.cursor_begin, context->current.preedit.cursor_end);
}
if (context->current.commit_text) {
wlr_text_input_v3_send_commit_string(text_input->text_input_v3,
context->current.commit_text);
}
if (context->current.delete.before_length || context->current.delete.after_length) {
wlr_text_input_v3_send_delete_surrounding_text(text_input->text_input_v3,
context->current.delete.before_length,
context->current.delete.after_length);
}
wlr_text_input_v3_send_done(text_input->text_input_v3);
} else if (text_input->text_input_v2) {
text_input_v2_send_preedit_string(text_input->text_input_v2, context->current.preedit.text,
context->current.preedit.cursor_begin);
if (context->current.commit_text) {
text_input_v2_send_commit_string(text_input->text_input_v2,
context->current.commit_text);
}
if (context->current.delete.before_length || context->current.delete.after_length) {
text_input_v2_send_delete_surrounding_text(
text_input->text_input_v2, context->current.preedit.text,
context->current.delete.before_length, context->current.delete.after_length);
}
} else {
text_input_v1_send_preedit_string(text_input->text_input_v1, context->current.preedit.text,
context->current.preedit.cursor_begin);
if (context->current.commit_text) {
text_input_v1_send_commit_string(text_input->text_input_v1,
context->current.commit_text);
}
if (context->current.delete.before_length || context->current.delete.after_length) {
text_input_v1_send_delete_surrounding_text(
text_input->text_input_v1, context->current.preedit.text,
context->current.delete.before_length, context->current.delete.after_length);
}
}
}
static void handle_input_method_keyboard_grab_destroy(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay =
wl_container_of(listener, relay, input_method_keyboard_grab_destroy);
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
wl_list_remove(&relay->input_method_keyboard_grab_destroy.link);
if (keyboard_grab->keyboard) {
// send modifier state to original client
wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat,
&keyboard_grab->keyboard->modifiers);
}
}
static void handle_input_method_grab_keyboard(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, input_method_grab_keyboard);
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data;
// send modifier state to grab
struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat);
wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, active_keyboard);
relay->input_method_keyboard_grab_destroy.notify = handle_input_method_keyboard_grab_destroy;
wl_signal_add(&keyboard_grab->events.destroy, &relay->input_method_keyboard_grab_destroy);
}
static void handle_input_method_destroy(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, input_method_destroy);
struct wlr_input_method_v2 *context = data;
assert(context == relay->wlr_input_method);
relay->wlr_input_method = NULL;
wl_list_remove(&relay->input_method_commit.link);
wl_list_remove(&relay->input_method_grab_keyboard.link);
wl_list_remove(&relay->input_method_destroy.link);
wl_list_remove(&relay->new_popup_surface.link);
struct text_input *text_input = relay_get_focused_text_input(relay);
if (text_input) {
// keyboard focus is still there, so keep the surface at hand in case
// the input method returns
struct wlr_surface *focused_surface = text_input_focused_surface(text_input);
text_input_set_pending_focused_surface(text_input, focused_surface);
text_input_send_leave(text_input);
}
}
static void handle_input_surface_map(struct wl_listener *listener, void *data)
{
struct input_popup *popup = wl_container_of(listener, popup, surface_map);
input_popup_update(popup, popup->relay->seat);
ky_scene_node_set_enabled(popup->surface_node, true);
}
static void handle_input_surface_unmap(struct wl_listener *listener, void *data)
{
struct input_popup *popup = wl_container_of(listener, popup, surface_unmap);
ky_scene_node_set_enabled(popup->surface_node, false);
}
static void handle_input_surface_commit(struct wl_listener *listener, void *data)
{
struct input_popup *popup = wl_container_of(listener, popup, surface_commit);
input_popup_update(popup, popup->relay->seat);
}
static void handle_input_popup_destroy(struct wl_listener *listener, void *data)
{
struct input_popup *popup = wl_container_of(listener, popup, popup_surface_destroy);
wl_list_remove(&popup->popup_surface_destroy.link);
wl_list_remove(&popup->surface_commit.link);
wl_list_remove(&popup->surface_unmap.link);
wl_list_remove(&popup->surface_map.link);
wl_list_remove(&popup->link);
/* surface destroy signal is bebind this, destroy scene here */
ky_scene_node_destroy(popup->surface_node);
free(popup);
}
static bool input_popup_hover(struct seat *seat, struct ky_scene_node *node, double x, double y,
uint32_t time, bool first, bool hold, void *data)
{
struct wlr_surface *surface = wlr_surface_try_from_node(node);
seat_notify_motion(seat, surface, time, x, y, first);
return false;
}
static void input_popup_click(struct seat *seat, struct ky_scene_node *node, uint32_t button,
bool pressed, uint32_t time, enum click_state state, void *data)
{
seat_notify_button(seat, time, button, pressed);
}
static void input_popup_leave(struct seat *seat, struct ky_scene_node *node, bool last, void *data)
{
struct wlr_surface *surface = wlr_surface_try_from_node(node);
seat_notify_leave(seat, surface);
}
static const struct input_event_node_impl input_popup_event_node_impl = {
.hover = input_popup_hover,
.click = input_popup_click,
.leave = input_popup_leave,
};
static struct ky_scene_node *input_popup_get_root(void *data)
{
struct input_popup *popup = data;
return popup->surface_node;
}
static void handle_new_popup_surface(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, new_popup_surface);
struct wlr_input_popup_surface_v2 *popup_surface = data;
struct input_popup *popup = calloc(1, sizeof(*popup));
if (!popup) {
return;
}
popup->relay = relay;
popup->popup_surface = popup_surface;
wl_list_insert(&relay->input_popups, &popup->link);
/* create a scene node to show the popup */
struct view_layer *layer = view_manager_get_layer(LAYER_POPUP, false);
struct ky_scene_surface *scene_surface =
ky_scene_surface_create(layer->tree, popup_surface->surface);
popup->surface_node = &scene_surface->buffer->node;
input_event_node_create(popup->surface_node, &input_popup_event_node_impl, input_popup_get_root,
NULL, popup);
ky_scene_node_set_enabled(popup->surface_node, popup_surface->surface->mapped);
popup->surface_map.notify = handle_input_surface_map;
wl_signal_add(&popup->popup_surface->surface->events.map, &popup->surface_map);
popup->surface_unmap.notify = handle_input_surface_unmap;
wl_signal_add(&popup->popup_surface->surface->events.unmap, &popup->surface_unmap);
popup->surface_commit.notify = handle_input_surface_commit;
wl_signal_add(&popup->popup_surface->surface->events.commit, &popup->surface_commit);
popup->popup_surface_destroy.notify = handle_input_popup_destroy;
wl_signal_add(&popup->popup_surface->events.destroy, &popup->popup_surface_destroy);
/* update popup position when surface is mapped */
input_popup_update(popup, relay->seat);
}
static void handle_new_input_method(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, new_input_method);
struct wlr_input_method_v2 *input_method = data;
if (relay->seat->wlr_seat != input_method->seat) {
return;
}
if (relay->wlr_input_method) {
kywc_log(KYWC_WARN, "Attempted to connect second input method to a seat");
wlr_input_method_v2_send_unavailable(input_method);
return;
}
relay->wlr_input_method = input_method;
relay->input_method_commit.notify = handle_input_method_commit;
wl_signal_add(&relay->wlr_input_method->events.commit, &relay->input_method_commit);
relay->input_method_grab_keyboard.notify = handle_input_method_grab_keyboard;
wl_signal_add(&relay->wlr_input_method->events.grab_keyboard,
&relay->input_method_grab_keyboard);
relay->input_method_destroy.notify = handle_input_method_destroy;
wl_signal_add(&relay->wlr_input_method->events.destroy, &relay->input_method_destroy);
relay->new_popup_surface.notify = handle_new_popup_surface;
wl_signal_add(&relay->wlr_input_method->events.new_popup_surface, &relay->new_popup_surface);
/* if has a pending focused text_inputs */
struct text_input *text_input;
wl_list_for_each(text_input, &relay->text_inputs, link) {
if (!text_input->pending_focused_surface) {
continue;
}
text_input_send_enter(text_input, text_input->pending_focused_surface);
text_input_set_pending_focused_surface(text_input, NULL);
break;
}
if (relay->pending_enabled_text_input) {
wlr_input_method_v2_send_activate(input_method);
relay_send_input_method_state(relay, relay->pending_enabled_text_input);
relay->pending_enabled_text_input = NULL;
}
}
static void handle_input_method_v2_destroy(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, input_method_v2_destroy);
wl_list_remove(&relay->input_method_v2_destroy.link);
wl_list_remove(&relay->new_input_method.link);
wl_list_init(&relay->input_method_v2_destroy.link);
wl_list_init(&relay->new_input_method.link);
}
static void handle_text_input_v2_destroy(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, text_input_v2_destroy);
wl_list_remove(&relay->text_input_v2_destroy.link);
wl_list_remove(&relay->new_text_input_v2.link);
wl_list_init(&relay->text_input_v2_destroy.link);
wl_list_init(&relay->new_text_input_v2.link);
}
static void handle_text_input_v3_destroy(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, text_input_v3_destroy);
wl_list_remove(&relay->text_input_v3_destroy.link);
wl_list_remove(&relay->new_text_input_v3.link);
wl_list_init(&relay->text_input_v3_destroy.link);
wl_list_init(&relay->new_text_input_v3.link);
}
static void handle_seat_destroy(struct wl_listener *listener, void *data)
{
struct input_method_relay *relay = wl_container_of(listener, relay, seat_destroy);
wl_list_remove(&relay->seat_destroy.link);
handle_input_method_v2_destroy(&relay->input_method_v2_destroy, NULL);
handle_text_input_v2_destroy(&relay->text_input_v2_destroy, NULL);
handle_text_input_v3_destroy(&relay->text_input_v3_destroy, NULL);
if (relay->wlr_input_method) {
handle_input_method_destroy(&relay->input_method_destroy, relay->wlr_input_method);
}
struct text_input *input, *tmp_input;
wl_list_for_each_safe(input, tmp_input, &relay->text_inputs, link) {
wl_list_remove(&input->link);
wl_list_init(&input->link);
}
struct input_popup *popup, *tmp_popup;
wl_list_for_each_safe(popup, tmp_popup, &relay->input_popups, link) {
wl_list_remove(&popup->link);
wl_list_init(&popup->link);
}
free(relay);
}
static void handle_new_seat(struct wl_listener *listener, void *data)
{
struct input_method_manager *manager = wl_container_of(listener, manager, new_seat);
struct seat *seat = data;
/* create input_manager for this seat */
struct input_method_relay *relay = calloc(1, sizeof(*relay));
if (!relay) {
return;
}
wl_list_init(&relay->text_inputs);
wl_list_init(&relay->input_popups);
seat->relay = relay;
relay->seat = seat;
relay->seat_destroy.notify = handle_seat_destroy;
wl_signal_add(&seat->events.destroy, &relay->seat_destroy);
relay->new_input_method.notify = handle_new_input_method;
wl_signal_add(&manager->input_method->events.input_method, &relay->new_input_method);
relay->input_method_v2_destroy.notify = handle_input_method_v2_destroy;
wl_signal_add(&manager->input_method->events.destroy, &relay->input_method_v2_destroy);
relay->new_text_input_v2.notify = handle_new_text_input_v2;
wl_signal_add(&manager->text_input_v2->events.text_input, &relay->new_text_input_v2);
relay->text_input_v2_destroy.notify = handle_text_input_v2_destroy;
wl_signal_add(&manager->text_input_v2->events.destroy, &relay->text_input_v2_destroy);
relay->new_text_input_v3.notify = handle_new_text_input_v3;
wl_signal_add(&manager->text_input_v3->events.text_input, &relay->new_text_input_v3);
relay->text_input_v3_destroy.notify = handle_text_input_v3_destroy;
wl_signal_add(&manager->text_input_v3->events.destroy, &relay->text_input_v3_destroy);
}
static void handle_text_input_v1_destroy(struct wl_listener *listener, void *data)
{
struct input_method_manager *manager =
wl_container_of(listener, manager, text_input_v1_destroy);
wl_list_remove(&manager->text_input_v1_destroy.link);
wl_list_remove(&manager->new_text_input_v1.link);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct input_method_manager *manager = wl_container_of(listener, manager, server_destroy);
wl_list_remove(&manager->server_destroy.link);
wl_list_remove(&manager->new_seat.link);
free(manager);
}
bool input_method_manager_create(struct input_manager *input_manager)
{
struct input_method_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->input_method = wlr_input_method_manager_v2_create(input_manager->server->display);
manager->text_input_v3 = wlr_text_input_manager_v3_create(input_manager->server->display);
manager->text_input_v2 = text_input_manager_v2_create(input_manager->server->display);
/* no seat in text_input_v1 create_text_input request */
manager->text_input_v1 = text_input_manager_v1_create(input_manager->server->display);
manager->new_text_input_v1.notify = handle_new_text_input_v1;
wl_signal_add(&manager->text_input_v1->events.text_input, &manager->new_text_input_v1);
manager->text_input_v1_destroy.notify = handle_text_input_v1_destroy;
wl_signal_add(&manager->text_input_v1->events.destroy, &manager->text_input_v1_destroy);
manager->new_seat.notify = handle_new_seat;
wl_signal_add(&input_manager->events.new_seat, &manager->new_seat);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(input_manager->server, &manager->server_destroy);
return true;
}
static bool text_input_match_surface(struct text_input *text_input, struct wlr_surface *surface)
{
if (text_input->text_input_v3) {
return wl_resource_get_client(text_input->text_input_v3->resource) ==
wl_resource_get_client(surface->resource);
} else if (text_input->text_input_v2) {
return wl_resource_get_client(text_input->text_input_v2->resource) ==
wl_resource_get_client(surface->resource);
} else {
return wl_resource_get_client(text_input->text_input_v1->resource) ==
wl_resource_get_client(surface->resource) &&
text_input->text_input_v1->surface == surface;
}
}
void input_method_set_focus(struct seat *seat, struct wlr_surface *surface)
{
struct input_method_relay *relay = seat->relay;
struct wlr_surface *focused_surface;
struct text_input *text_input;
wl_list_for_each(text_input, &relay->text_inputs, link) {
focused_surface = text_input_focused_surface(text_input);
if (text_input->pending_focused_surface) {
if (surface != text_input->pending_focused_surface) {
text_input_set_pending_focused_surface(text_input, NULL);
}
} else if (focused_surface) {
if (surface != focused_surface) {
relay_disable_text_input(relay, text_input);
text_input_send_leave(text_input);
} else {
kywc_log(KYWC_DEBUG, "IM relay set_focus already focused");
continue;
}
}
if (surface && text_input_match_surface(text_input, surface)) {
if (relay->wlr_input_method) {
text_input_send_enter(text_input, surface);
} else {
text_input_set_pending_focused_surface(text_input, surface);
}
}
}
}
static struct wlr_input_method_keyboard_grab_v2 *
keyboard_get_input_method_grab(struct keyboard *keyboard)
{
struct wlr_input_method_v2 *input_method = keyboard->seat->relay->wlr_input_method;
struct wlr_virtual_keyboard_v1 *virtual_keyboard =
wlr_input_device_get_virtual_keyboard(&keyboard->wlr_keyboard->base);
if (!input_method || !input_method->keyboard_grab ||
(virtual_keyboard && wl_resource_get_client(virtual_keyboard->resource) ==
wl_resource_get_client(input_method->keyboard_grab->resource))) {
return NULL;
}
return input_method->keyboard_grab;
}
bool input_method_handle_key(struct keyboard *keyboard, uint32_t time, uint32_t key, uint32_t state)
{
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab =
keyboard_get_input_method_grab(keyboard);
if (!keyboard_grab) {
return false;
}
wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, keyboard->wlr_keyboard);
wlr_input_method_keyboard_grab_v2_send_key(keyboard_grab, time, key, state);
return true;
}
bool input_method_handle_modifiers(struct keyboard *keyboard)
{
struct wlr_input_method_keyboard_grab_v2 *keyboard_grab =
keyboard_get_input_method_grab(keyboard);
if (!keyboard_grab) {
return false;
}
wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, keyboard->wlr_keyboard);
wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab,
&keyboard->wlr_keyboard->modifiers);
return true;
}
bool keyboard_is_from_input_method(struct keyboard *keyboard)
{
struct wlr_input_method_v2 *input_method = keyboard->seat->relay->wlr_input_method;
struct wlr_virtual_keyboard_v1 *virtual_keyboard =
wlr_input_device_get_virtual_keyboard(&keyboard->wlr_keyboard->base);
return input_method && virtual_keyboard &&
wl_resource_get_client(virtual_keyboard->resource) ==
wl_resource_get_client(input_method->resource);
}
kylin-wayland-compositor/src/input/touch.c 0000664 0001750 0001750 00000053567 15160461067 017721 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include
#include
#include "input/seat.h"
#include "input_p.h"
#include "scene/surface.h"
#include "server.h"
#include "util/macros.h"
#include "view/view.h"
#include "xwayland.h"
#define TOUCH_HOLD_TIMEOUT (50)
#define TOUCH_FILTER_TIMEOUT (100)
struct touch_manager {
struct wl_listener new_input;
struct wl_listener server_destroy;
};
struct touch {
struct wlr_touch *wlr_touch;
struct input *input;
struct wl_listener input_destroy;
struct wl_list points;
uint32_t points_count;
/* timer for gesture detect filter */
struct wl_event_source *filter;
bool filter_enabled;
/* current gesture state per touch */
struct gesture_state gestures;
uint32_t hold_points;
/* hold canceled status */
uint32_t hold_canceled;
/* pinch angle */
double angle;
};
struct touch_point {
struct touch *touch;
struct wl_list link;
struct wlr_surface *surface;
struct wl_listener surface_unmap;
/* timer for hold gesture */
struct wl_event_source *timer;
bool hold, moved;
double abs_x, abs_y;
double last_x, last_y;
double dx, dy;
int32_t touch_id;
uint32_t directions;
};
static struct touch *touch_from_wlr_touch(struct wlr_touch *wlr_touch)
{
return wlr_touch->data;
}
static void touch_handle_input_destroy(struct wl_listener *listener, void *data)
{
struct touch *touch = wl_container_of(listener, touch, input_destroy);
wl_list_remove(&touch->input_destroy.link);
gesture_state_finish(&touch->gestures);
wl_event_source_remove(touch->filter);
struct touch_point *point, *tmp;
wl_list_for_each_safe(point, tmp, &touch->points, link) {
wl_list_remove(&point->link);
if (point->surface) {
wl_list_remove(&point->surface_unmap.link);
}
if (point->timer) {
wl_event_source_remove(point->timer);
}
free(point);
}
free(touch);
}
static enum gesture_edge touch_calc_edge(struct touch *touch)
{
double abs_avg_x = 0.0;
double abs_avg_y = 0.0;
struct touch_point *point;
wl_list_for_each(point, &touch->points, link) {
/* meet the first free point */
if (point->touch_id < 0) {
break;
}
abs_avg_x += point->abs_x;
abs_avg_y += point->abs_y;
}
abs_avg_x /= touch->points_count;
abs_avg_y /= touch->points_count;
// TODO: edge normalize by physical size of touch
enum gesture_edge edge = GESTURE_EDGE_NONE;
if (abs_avg_y <= 0.05) {
edge = GESTURE_EDGE_TOP;
}
if (abs_avg_x <= 0.05) {
edge = GESTURE_EDGE_LEFT;
}
if (abs_avg_x >= 0.95) {
edge = GESTURE_EDGE_RIGHT;
}
if (abs_avg_y >= 0.95) {
edge = GESTURE_EDGE_BOTTOM;
}
return edge;
}
static bool touch_gesture_begin(struct touch *touch, enum gesture_type gesture, uint8_t fingers)
{
struct gesture_state *state = &touch->gestures;
if (state->type == gesture && state->fingers == fingers) {
return false;
}
/* cancel current gesture and enter the new hold */
if (state->type != GESTURE_TYPE_NONE) {
gesture_state_end(state, state->type, state->device, true);
}
if (gesture == GESTURE_TYPE_NONE) {
return false;
}
enum gesture_edge edge = touch_calc_edge(touch);
gesture_state_begin(state, gesture, GESTURE_DEVICE_TOUCHSCREEN, edge, fingers);
return true;
}
static void touch_gesture_detect(struct touch *touch)
{
if (touch->points_count == 0) {
return;
}
bool all_points_moved = true;
struct touch_point *point;
wl_list_for_each(point, &touch->points, link) {
/* meet the first free point */
if (point->touch_id < 0) {
break;
}
if (!point->moved) {
all_points_moved = false;
break;
}
}
if (!all_points_moved) {
/* hold gesture if all hold points not moved */
if (touch->hold_points && !touch->hold_canceled) {
if (!touch_gesture_begin(touch, GESTURE_TYPE_HOLD, touch->hold_points)) {
/* cancel the hold gesture when touch point is moved,
* except for there is only one touch point
*/
if (touch->hold_points != 1) {
gesture_state_end(&touch->gestures, GESTURE_TYPE_HOLD,
GESTURE_DEVICE_TOUCHSCREEN, true);
}
touch->hold_canceled = true;
}
}
return;
}
/* moved when one touch point swipe from edge */
if (touch->points_count == 1) {
touch_gesture_begin(touch, GESTURE_TYPE_SWIPE, 1);
return;
}
/* swipre or pinch check */
wl_list_for_each(point, &touch->points, link) {
/* meet the first free point */
if (point->touch_id < 0) {
break;
}
if (!point->moved) {
all_points_moved = false;
break;
}
}
bool one_direction = true;
uint32_t directions = GESTURE_DIRECTION_NONE;
wl_list_for_each(point, &touch->points, link) {
/* meet the first free point */
if (point->touch_id < 0) {
break;
}
if (directions == GESTURE_DIRECTION_NONE) {
directions = point->directions;
} else if (directions != point->directions) {
one_direction = false;
break;
}
}
touch_gesture_begin(touch, one_direction ? GESTURE_TYPE_SWIPE : GESTURE_TYPE_PINCH,
touch->points_count);
}
static void touch_filter_enable(struct touch *touch, bool enabled)
{
wl_event_source_timer_update(touch->filter, enabled ? TOUCH_FILTER_TIMEOUT : 0);
touch->filter_enabled = enabled;
}
static int touch_handle_timer(void *data)
{
struct touch *touch = data;
touch->filter_enabled = false;
touch_gesture_detect(touch);
return 0;
}
static void handle_new_input(struct wl_listener *listener, void *data)
{
struct touch_manager *manager = wl_container_of(listener, manager, new_input);
struct input *input = data;
/* input has been configured, only care about touch */
if (input->prop.type != WLR_INPUT_DEVICE_TOUCH) {
return;
}
struct touch *touch = calloc(1, sizeof(*touch));
if (!touch) {
return;
}
struct wl_display *display = input->seat->wlr_seat->display;
struct wl_event_loop *loop = wl_display_get_event_loop(display);
touch->filter = wl_event_loop_add_timer(loop, touch_handle_timer, touch);
if (!touch->filter) {
free(touch);
return;
}
touch->input = input;
touch->input_destroy.notify = touch_handle_input_destroy;
wl_signal_add(&input->events.destroy, &touch->input_destroy);
touch->wlr_touch = wlr_touch_from_input_device(input->wlr_input);
touch->wlr_touch->data = touch;
wl_list_init(&touch->points);
gesture_state_init(&touch->gestures, display);
}
void touch_reset_gesture(struct input_manager *input_manager)
{
struct input *input = NULL;
wl_list_for_each(input, &input_manager->inputs, link) {
if (input->prop.type != WLR_INPUT_DEVICE_TOUCH) {
continue;
}
struct wlr_touch *wlr_touch = wlr_touch_from_input_device(input->wlr_input);
struct touch *touch = wlr_touch->data;
touch_filter_enable(touch, false);
struct touch_point *point;
wl_list_for_each(point, &touch->points, link) {
/* meet the first free point */
if (point->touch_id < 0) {
break;
}
if (point->timer) {
wl_event_source_timer_update(point->timer, 0);
}
}
gesture_state_end(&touch->gestures, touch->gestures.type, GESTURE_DEVICE_TOUCHSCREEN, true);
}
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct touch_manager *manager = wl_container_of(listener, manager, server_destroy);
wl_list_remove(&manager->server_destroy.link);
wl_list_remove(&manager->new_input.link);
free(manager);
}
bool touch_manager_create(struct input_manager *input_manager)
{
struct touch_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->new_input.notify = handle_new_input;
input_add_new_listener(&manager->new_input);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(input_manager->server, &manager->server_destroy);
return true;
}
static struct wlr_surface *touch_get_surface(struct touch *touch, double *sx, double *sy,
struct wlr_surface **toplevel)
{
struct seat *seat = touch->input->seat;
struct cursor *cursor = seat->cursor;
struct ky_scene_node *node =
ky_scene_node_at(&seat->scene->tree.node, cursor->lx, cursor->ly, sx, sy);
if (!node) {
return NULL;
}
if (toplevel) {
*toplevel = input_event_node_toplevel(input_event_node_from_node(node));
}
return wlr_surface_try_from_node(node);
}
static int touch_point_handle_timer(void *data)
{
struct touch_point *point = data;
point->hold = true;
point->touch->hold_points++;
if (point->touch->filter_enabled) {
return 0;
}
if (point->touch->gestures.type == GESTURE_TYPE_NONE) {
touch_filter_enable(point->touch, true);
return 0;
}
/* keep hold gesture */
if (point->touch->gestures.type == GESTURE_TYPE_HOLD) {
touch_gesture_begin(point->touch, GESTURE_TYPE_HOLD, point->touch->hold_points);
}
return 0;
}
static void touch_point_handle_surface_unmap(struct wl_listener *listener, void *data)
{
struct touch_point *point = wl_container_of(listener, point, surface_unmap);
wl_list_remove(&point->surface_unmap.link);
point->surface = NULL;
}
static struct touch_point *touch_point_create(struct touch *touch, int32_t touch_id)
{
struct touch_point *point, *free_point = NULL;
wl_list_for_each(point, &touch->points, link) {
if (point->touch_id == touch_id) {
return point;
}
if (!free_point && point->touch_id < 0) {
free_point = point;
}
}
/* not found, reuse the first free one */
if (free_point) {
free_point->touch_id = touch_id;
return free_point;
}
/* alloc one if all in used */
point = calloc(1, sizeof(*point));
if (!point) {
return NULL;
}
point->touch_id = touch_id;
point->touch = touch;
point->surface_unmap.notify = touch_point_handle_surface_unmap;
wl_list_insert(touch->points.prev, &point->link);
struct wl_display *display = touch->input->seat->wlr_seat->display;
struct wl_event_loop *loop = wl_display_get_event_loop(display);
point->timer = wl_event_loop_add_timer(loop, touch_point_handle_timer, point);
return point;
}
static struct touch_point *touch_point_from_id(struct touch *touch, int32_t touch_id)
{
struct touch_point *point;
wl_list_for_each(point, &touch->points, link) {
if (point->touch_id == touch_id) {
return point;
}
}
return NULL;
}
static void touch_point_reset(struct touch_point *point, bool cancelled)
{
struct gesture_state *state = &point->touch->gestures;
if (state->type != GESTURE_TYPE_NONE) {
/* touch point up to set hold canceled in hold state,
* and cancels gesture when touch point count is more than 1
*/
if (state->type == GESTURE_TYPE_HOLD) {
point->touch->hold_canceled = true;
if (!cancelled && point->touch->points_count > 1) {
cancelled = true;
}
}
gesture_state_end(state, state->type, state->device, cancelled);
}
point->touch_id = -1;
point->touch->points_count--;
if (point->surface) {
point->surface = NULL;
wl_list_remove(&point->surface_unmap.link);
}
if (point->timer) {
wl_event_source_timer_update(point->timer, 0);
}
if (point->hold) {
point->touch->hold_points--;
point->hold = false;
}
/* last touch point up reset the hold canceled state */
if (!point->touch->points_count) {
point->touch->hold_canceled = false;
}
/* reinsert to tail */
wl_list_remove(&point->link);
wl_list_insert(point->touch->points.prev, &point->link);
}
bool touch_handle_down(struct wlr_touch_down_event *event)
{
struct touch *touch = touch_from_wlr_touch(event->touch);
if (!touch) {
return false;
}
struct seat *seat = touch->input->seat;
cursor_move(seat->cursor, &event->touch->base, event->x, event->y, false, true);
if (seat->touch_grab && seat->touch_grab->interface->touch &&
seat->touch_grab->interface->touch(seat->touch_grab, event->time_msec, true)) {
return true;
}
struct touch_point *point = touch_point_create(touch, event->touch_id);
if (point->timer) {
wl_event_source_timer_update(point->timer, TOUCH_HOLD_TIMEOUT);
}
touch->points_count++;
point->abs_x = point->last_x = event->x;
point->abs_y = point->last_y = event->y;
point->moved = false;
touch->angle = 0.0;
struct wlr_surface *toplevel = NULL;
double sx, sy;
struct wlr_surface *surface = touch_get_surface(touch, &sx, &sy, &toplevel);
if (!surface || !wlr_surface_accepts_touch(surface, seat->wlr_seat)) {
return false;
}
point->surface = surface;
wl_signal_add(&surface->events.unmap, &point->surface_unmap);
if (xwayland_check_client(wl_resource_get_client(point->surface->resource))) {
sx = xwayland_scale(sx);
sy = xwayland_scale(sy);
}
wlr_seat_touch_notify_down(seat->wlr_seat, surface, event->time_msec, event->touch_id, sx, sy);
/* activate and focus the toplevel surface */
if (toplevel) {
struct view *view = view_try_from_wlr_surface(toplevel);
if (view) {
kywc_view_activate(&view->base);
view_set_focus(view, seat);
} else {
seat_focus_surface(seat, toplevel);
}
}
return true;
}
static uint32_t touch_point_calc_directions(struct touch_point *point, double dx, double dy)
{
uint32_t directions = GESTURE_DIRECTION_NONE;
if (fabs(dx) > fabs(dy)) {
if (dx > 0) {
directions |= GESTURE_DIRECTION_RIGHT;
} else {
directions |= GESTURE_DIRECTION_LEFT;
}
} else {
if (dy > 0) {
directions |= GESTURE_DIRECTION_DOWN;
} else {
directions |= GESTURE_DIRECTION_UP;
}
}
return directions;
}
static void touch_calc_average_delta(struct touch *touch, double *dx, double *dy)
{
double total_dx = 0, total_dy = 0;
struct touch_point *point;
wl_list_for_each(point, &touch->points, link) {
/* meet the first free point */
if (point->touch_id < 0) {
break;
}
total_dx += point->dx;
total_dy += point->dy;
}
*dx = total_dx / touch->points_count;
*dy = total_dy / touch->points_count;
}
static double touch_calc_average_scale(struct touch *touch)
{
double start_min = 1.0, current_min = 1.0;
double start_max = 0.0, current_max = 0.0;
struct touch_point *point;
wl_list_for_each(point, &touch->points, link) {
/* meet the first free point */
if (point->touch_id < 0) {
break;
}
if (start_min == 1.0 && start_max == 0.0) {
start_min = start_max = point->abs_x;
current_min = current_max = point->last_x;
} else {
start_min = MIN(start_min, point->abs_x);
start_max = MAX(start_max, point->abs_x);
current_min = MIN(current_min, point->last_x);
current_max = MAX(current_max, point->last_x);
}
}
double start_width = start_max - start_min;
double current_width = current_max - current_min;
if (start_width == 0.0) {
kywc_log(KYWC_WARN, "Start(max = %f, min = %f), Current(max = %f, min = %f)", start_max,
start_min, current_max, current_min);
return 1.0;
}
return current_width / start_width;
}
static double touch_calc_average_angle_delta(struct touch *touch)
{
double dx = 0.0;
double dy = 0.0;
struct touch_point *point;
wl_list_for_each(point, &touch->points, link) {
/* meet the first free point */
if (point->touch_id < 0) {
break;
}
dx = point->last_x - dx;
dy = point->last_y - dy;
}
double tangle = atan2(dy, dx) * 180.0 / 3.14;
if (touch->angle == 0.0) {
touch->angle = tangle;
}
double angle_delta = tangle - touch->angle;
if (angle_delta > 180.0) {
angle_delta -= 360.0;
} else if (angle_delta < -180.0) {
angle_delta += 360.0;
}
touch->angle = tangle;
return angle_delta;
}
void touch_handle_motion(struct wlr_touch_motion_event *event, bool handle)
{
struct touch *touch = touch_from_wlr_touch(event->touch);
if (!touch) {
return;
}
struct seat *seat = touch->input->seat;
cursor_move(seat->cursor, &event->touch->base, event->x, event->y, false, true);
if (seat->touch_grab && seat->touch_grab->interface->motion &&
seat->touch_grab->interface->motion(seat->touch_grab, event->time_msec, seat->cursor->lx,
seat->cursor->ly)) {
return;
}
struct touch_point *point = touch_point_from_id(touch, event->touch_id);
if (!point) {
return;
}
point->dx = event->x - point->last_x;
point->dy = event->y - point->last_y;
point->last_x = event->x;
point->last_y = event->y;
double dx = event->x - point->abs_x;
double dy = event->y - point->abs_y;
point->moved = fabs(dx) > 0.01f || fabs(dy) > 0.01f;
/* calc point motion directions */
point->directions = touch_point_calc_directions(point, dx, dy);
if (!touch->filter_enabled) {
if (!touch->gestures.handled) {
touch_gesture_detect(touch);
}
if (touch->gestures.type == GESTURE_TYPE_SWIPE) {
double avg_dx = 0, avg_dy = 0;
touch_calc_average_delta(touch, &avg_dx, &avg_dy);
gesture_state_update(&touch->gestures, GESTURE_TYPE_SWIPE, GESTURE_DEVICE_TOUCHSCREEN,
avg_dx, avg_dy, NAN, NAN);
} else if (touch->gestures.type == GESTURE_TYPE_PINCH) {
double avg_dx = 0, avg_dy = 0;
touch_calc_average_delta(touch, &avg_dx, &avg_dy);
double scale = touch_calc_average_scale(touch);
double angle_delta = touch_calc_average_angle_delta(touch);
gesture_state_update(&touch->gestures, GESTURE_TYPE_PINCH, GESTURE_DEVICE_TOUCHSCREEN,
avg_dx, avg_dy, scale, angle_delta);
}
}
if (!handle || !point->surface) {
return;
}
double sx, sy;
struct wlr_surface *surface = touch_get_surface(touch, &sx, &sy, NULL);
/* if motion out of point->surface */
if (surface != point->surface) {
if (!seat_is_dragging(seat)) {
int lx, ly;
struct ky_scene_buffer *scene_buffer = ky_scene_buffer_try_from_surface(point->surface);
ky_scene_node_coords(&scene_buffer->node, &lx, &ly);
sx = seat->cursor->lx - lx;
sy = seat->cursor->ly - ly;
} else if (surface) {
wlr_seat_touch_point_focus(seat->wlr_seat, surface, event->time_msec, event->touch_id,
sx, sy);
}
}
if (xwayland_check_client(wl_resource_get_client(point->surface->resource))) {
sx = xwayland_scale(sx);
sy = xwayland_scale(sy);
}
selection_handle_cursor_move(seat, seat->cursor->lx, seat->cursor->ly);
wlr_seat_touch_notify_motion(seat->wlr_seat, event->time_msec, event->touch_id, sx, sy);
}
void touch_handle_up(struct wlr_touch_up_event *event, bool handle)
{
struct touch *touch = touch_from_wlr_touch(event->touch);
if (!touch) {
return;
}
struct touch_point *point = touch_point_from_id(touch, event->touch_id);
if (point) {
touch_point_reset(point, false);
}
struct seat *seat = touch->input->seat;
if (seat->touch_grab && seat->touch_grab->interface->touch &&
seat->touch_grab->interface->touch(seat->touch_grab, event->time_msec, false)) {
return;
}
if (!point) {
return;
}
/* workaround: ukui-panel need to send motion event when drop */
if (seat_is_dragging(seat)) {
cursor_feed_motion(seat->cursor, event->time_msec, &event->touch->base, 0, 0, 0, 0);
wlr_seat_pointer_notify_frame(seat->wlr_seat);
}
if (handle) {
wlr_seat_touch_notify_up(seat->wlr_seat, event->time_msec, event->touch_id);
}
}
void touch_handle_cancel(struct wlr_touch_cancel_event *event, bool handle)
{
struct touch *touch = touch_from_wlr_touch(event->touch);
if (!touch) {
return;
}
struct touch_point *point = touch_point_from_id(touch, event->touch_id);
if (point) {
touch_point_reset(point, true);
}
struct seat *seat = touch->input->seat;
if (seat->touch_grab && seat->touch_grab->interface->touch &&
seat->touch_grab->interface->touch(seat->touch_grab, event->time_msec, false)) {
return;
}
if (point && handle && point->surface) {
struct seat *seat = touch->input->seat;
struct wl_client *client = wl_resource_get_client(point->surface->resource);
struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client);
if (seat_client) {
wlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client);
}
}
}
kylin-wayland-compositor/src/input/action.c 0000664 0001750 0001750 00000107115 15160461067 020041 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include
#include
#include "config.h"
#include "input_p.h"
#include "server.h"
#include "util/dbus.h"
#include "util/macros.h"
#include "util/spawn.h"
#include "util/string.h"
enum action_type {
ACTION_TYPE_NONE = 0,
ACTION_TYPE_DBUS_ACTION,
ACTION_TYPE_RUN_COMMAND,
ACTION_TYPE_SEND_BUTTON,
ACTION_TYPE_SEND_KEY,
};
enum key_action {
KEY_ACTION_PRESS = 1 << 0,
KEY_ACTION_RELEASE = 1 << 1,
KEY_ACTION_CLICK = (1 << 2) - 1,
};
enum input_type { INPUT_TYPE_NONE = 0, INPUT_TYPE_KEYBOARD, INPUT_TYPE_GESTURE };
enum dbus_type { DBUS_TYPE_NONE = 0, DBUS_TYPE_SESSION, DBUS_TYPE_SYSTEM };
enum control_type { CONTROL_TYPE_DELETE, CONTROL_TYPE_ENABLE, CONTROL_TYPE_DISABLE };
struct action_dbus_data {
enum dbus_type type;
char *service;
char *path;
char *interface;
char *method;
char *param_type;
char *param_value;
};
struct action_command_data {
char *cmd;
};
struct action_button_data {
uint32_t val;
};
struct keycodes {
enum key_action action;
uint32_t *code;
uint32_t len;
};
struct action_key_data {
struct keycodes *modifiers;
struct keycodes *keys;
};
struct action_data {
enum action_type type;
enum key_binding_type binding_type;
bool enable;
union {
struct action_dbus_data dbus;
struct action_button_data button;
struct action_key_data key;
struct action_command_data command;
} data;
const char *desc;
};
struct input_action {
enum input_type type;
char *bindings;
struct action_data *action;
void *data; /* key or gesture binding */
struct wl_list link;
};
static struct input_action_manager {
struct config *config;
struct server *server;
struct wl_listener server_destroy;
struct wl_list actions;
} *manager = NULL;
static struct keycode_map {
const char *key;
uint32_t code;
} keycode_maps[] = {
{ "a", KEY_A },
{ "b", KEY_B },
{ "c", KEY_C },
{ "d", KEY_D },
{ "e", KEY_E },
{ "f", KEY_F },
{ "g", KEY_G },
{ "h", KEY_H },
{ "i", KEY_I },
{ "g", KEY_G },
{ "k", KEY_K },
{ "l", KEY_L },
{ "m", KEY_M },
{ "n", KEY_N },
{ "o", KEY_O },
{ "p", KEY_P },
{ "q", KEY_Q },
{ "r", KEY_R },
{ "s", KEY_S },
{ "t", KEY_T },
{ "u", KEY_U },
{ "v", KEY_V },
{ "w", KEY_W },
{ "x", KEY_X },
{ "y", KEY_Y },
{ "z", KEY_Z },
{ "Tab", KEY_TAB },
{ "Super_L", KEY_LEFTMETA },
{ "Alt_L", KEY_LEFTALT },
{ "Left", KEY_LEFT },
{ "Right", KEY_RIGHT },
{ "Down", KEY_DOWN },
{ "Up", KEY_UP },
{ "Shift_L", KEY_LEFTSHIFT },
{ "Control_R", KEY_RIGHTCTRL },
{ "Control_L", KEY_LEFTCTRL },
{ "Alt_R", KEY_RIGHTALT },
{ "Super_R", KEY_RIGHTMETA },
{ "Shift_R", KEY_RIGHTSHIFT },
};
static struct btncode_map {
const char *btn;
uint32_t code;
} btncode_maps[] = {
{ "left", BTN_LEFT }, { "right", BTN_RIGHT }, { "middle", BTN_MIDDLE },
{ "back", BTN_BACK }, { "forward", BTN_FORWARD },
};
static const char *service_path = "/com/kylin/Wlcom/InputAction";
static const char *service_interface = "com.kylin.Wlcom.InputAction";
static uint32_t keycode_map(const char *keystr)
{
for (size_t i = 0; i < ARRAY_SIZE(keycode_maps); i++) {
if (strcasecmp(keystr, keycode_maps[i].key) == 0) {
return keycode_maps[i].code;
}
}
return 0;
}
static const char *keycode_to_str(uint32_t code)
{
for (size_t i = 0; i < ARRAY_SIZE(keycode_maps); i++) {
if (keycode_maps[i].code == code) {
return keycode_maps[i].key;
}
}
return NULL;
}
static char *keycodes_to_str(struct keycodes *keycodes)
{
char *str = NULL;
for (uint32_t i = 0; i < keycodes->len; ++i) {
const char *keystr = keycode_to_str(keycodes->code[i]);
if (!keystr) {
continue;
}
if (!str) {
size_t len = strlen(keystr);
str = malloc(len + 1);
if (!str) {
return NULL;
}
memcpy(str, keystr, len);
str[len] = '\0';
} else {
int new_size = strlen(str) + strlen(keystr) + 2;
char *new_str = malloc(new_size);
if (!new_str) {
free(str);
return NULL;
}
snprintf(new_str, new_size, "%s+%s", str, keystr);
free(str);
str = new_str;
}
}
return str;
}
static uint32_t btncode_map(const char *btnstr)
{
for (size_t i = 0; i < ARRAY_SIZE(btncode_maps); i++) {
if (strcasecmp(btnstr, btncode_maps[i].btn) == 0) {
return btncode_maps[i].code;
}
}
return 0;
}
static const char *btncode_to_str(uint32_t code)
{
for (size_t i = 0; i < ARRAY_SIZE(btncode_maps); i++) {
if (code == btncode_maps[i].code) {
return btncode_maps[i].btn;
}
}
return NULL;
}
static struct keycodes *keycodes_create(const char *str)
{
struct keycodes *keycodes = calloc(1, sizeof(*keycodes));
if (!keycodes) {
return NULL;
}
size_t action_len = 0;
char **split_action = string_split(str, ":", &action_len);
if (action_len > 2) {
kywc_log(KYWC_ERROR, "Split key action error");
}
size_t len = 0;
char **split_str = string_split(split_action[0], "+", &len);
for (size_t i = 0, j = 0; i < len; i++) {
uint32_t keycode = keycode_map(split_str[i]);
if (!keycode) {
continue;
}
keycodes->code = realloc(keycodes->code, (keycodes->len + 1) * sizeof(uint32_t));
keycodes->code[j++] = keycode;
keycodes->len++;
}
string_free_split(split_str);
keycodes->action = KEY_ACTION_CLICK;
if (action_len == 2) {
if (strcmp(split_action[1], "press") == 0) {
keycodes->action = KEY_ACTION_PRESS;
} else if (strcmp(split_action[1], "release") == 0) {
keycodes->action = KEY_ACTION_RELEASE;
}
}
string_free_split(split_action);
return keycodes;
}
static int list_input_actions(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
struct input_action_manager *manager = userdata;
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(m, &reply));
CK(sd_bus_message_open_container(reply, 'a', "(ss)"));
struct input_action *action;
wl_list_for_each(action, &manager->actions, link) {
json_object *itype_obj = json_object_object_get(
manager->config->json, action->type == INPUT_TYPE_KEYBOARD ? "keyboard" : "gesture");
json_object *config = json_object_object_get(itype_obj, action->bindings);
const char *cfg = json_object_to_json_string(config);
sd_bus_message_append(reply, "(ss)", action->bindings, cfg);
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
static struct action_data *action_data_create_from_bus_string(const char *bus_str)
{
struct action_data *action_data = NULL;
size_t len = 0;
char **split_str = string_split(bus_str, ",", &len);
if (len < 2) {
goto err;
}
action_data = calloc(1, sizeof(*action_data));
if (!action_data) {
goto err;
}
if (strcmp(split_str[0], "dbus") == 0) {
action_data->type = ACTION_TYPE_DBUS_ACTION;
} else if (strcmp(split_str[0], "command") == 0) {
action_data->type = ACTION_TYPE_RUN_COMMAND;
} else if (strcmp(split_str[0], "button") == 0) {
action_data->type = ACTION_TYPE_SEND_BUTTON;
} else if (strcmp(split_str[0], "key") == 0) {
action_data->type = ACTION_TYPE_SEND_KEY;
} else {
action_data->type = ACTION_TYPE_NONE;
}
kywc_log(KYWC_DEBUG, "Action type: %s, len: %ld", split_str[0], len);
if (action_data->type == ACTION_TYPE_DBUS_ACTION && len == 6) {
if (strcmp(split_str[1], "session") == 0) {
action_data->data.dbus.type = DBUS_TYPE_SESSION;
} else if (strcmp(split_str[1], "system") == 0) {
action_data->data.dbus.type = DBUS_TYPE_SYSTEM;
} else {
action_data->data.dbus.type = DBUS_TYPE_NONE;
}
action_data->data.dbus.service = strdup(split_str[2]);
action_data->data.dbus.path = strdup(split_str[3]);
action_data->data.dbus.interface = strdup(split_str[4]);
action_data->data.dbus.method = strdup(split_str[5]);
} else if (action_data->type == ACTION_TYPE_RUN_COMMAND && len == 2) {
action_data->data.command.cmd = strdup(split_str[1]);
} else if (action_data->type == ACTION_TYPE_SEND_BUTTON && len == 2) {
action_data->data.button.val = btncode_map(split_str[1]);
} else if (action_data->type == ACTION_TYPE_SEND_KEY && len == 3) {
action_data->data.key.modifiers = keycodes_create(split_str[1]);
action_data->data.key.keys = keycodes_create(split_str[2]);
} else {
free(action_data);
action_data = NULL;
goto err;
}
action_data->enable = true;
err:
string_free_split(split_str);
return action_data;
}
static bool dbus_method_paramter_handle(enum dbus_type type, const char *service, const char *path,
const char *interface, const char *method,
const char *param_type, const char *param_value)
{
if (!param_type || !param_value) {
return type == DBUS_TYPE_SESSION
? dbus_call_method(service, path, interface, method, NULL, NULL)
: dbus_call_system_method(service, path, interface, method, NULL, NULL);
}
if (!strcmp(param_type, "s")) {
const char *value = param_value;
return type == DBUS_TYPE_SESSION
? dbus_call_methodv(service, path, interface, method, NULL, NULL, param_type,
value)
: dbus_call_system_methodv(service, path, interface, method, NULL, NULL,
param_type, value);
} else if (!strcmp(param_type, "b")) {
bool value = strcasecmp(param_value, "true") == 0;
return type == DBUS_TYPE_SESSION
? dbus_call_methodv(service, path, interface, method, NULL, NULL, param_type,
value)
: dbus_call_system_methodv(service, path, interface, method, NULL, NULL,
param_type, value);
} else if (!strcmp(param_type, "d")) {
double value = atof(param_value);
return type == DBUS_TYPE_SESSION
? dbus_call_methodv(service, path, interface, method, NULL, NULL, param_type,
value)
: dbus_call_system_methodv(service, path, interface, method, NULL, NULL,
param_type, value);
} else if (!strcmp(param_type, "i")) {
int value = atoi(param_value);
return type == DBUS_TYPE_SESSION
? dbus_call_methodv(service, path, interface, method, NULL, NULL, param_type,
value)
: dbus_call_system_methodv(service, path, interface, method, NULL, NULL,
param_type, value);
}
return false;
}
static void action_call_dbus_method(struct action_dbus_data *dbus_data)
{
bool ret = dbus_method_paramter_handle(dbus_data->type, dbus_data->service, dbus_data->path,
dbus_data->interface, dbus_data->method,
dbus_data->param_type, dbus_data->param_value);
if (!ret) {
kywc_log(KYWC_ERROR, "Dbus call failed: %s %s %s %s", dbus_data->service, dbus_data->path,
dbus_data->interface, dbus_data->method);
}
}
static void action_call_send_button(struct action_button_data *data)
{
struct seat *seat = input_manager_get_default_seat();
struct cursor *cursor = seat->cursor;
if (cursor->touch_simulation_pointer && cursor->last_click_button == BTN_LEFT &&
data->val == BTN_RIGHT) {
seat_feed_pointer_button(seat, BTN_LEFT, false);
cursor->touch_simulation_pointer = false;
}
seat_feed_pointer_button(seat, data->val, true);
seat_feed_pointer_button(seat, data->val, false);
}
static void action_call_send_key(struct action_key_data *data)
{
struct seat *seat = input_manager_get_default_seat();
for (uint32_t i = 0; data->modifiers && i < data->modifiers->len; ++i) {
if (data->modifiers->action & KEY_ACTION_PRESS) {
seat_feed_keyboard_key(seat, data->modifiers->code[i], true);
}
}
for (uint32_t i = 0; data->keys && i < data->keys->len; ++i) {
if (data->keys->action & KEY_ACTION_PRESS) {
seat_feed_keyboard_key(seat, data->keys->code[i], true);
}
}
for (uint32_t i = 0; data->modifiers && i < data->modifiers->len; ++i) {
if (data->modifiers->action & KEY_ACTION_RELEASE) {
seat_feed_keyboard_key(seat, data->modifiers->code[i], false);
}
}
for (uint32_t i = 0; data->keys && i < data->keys->len; ++i) {
if (data->keys->action & KEY_ACTION_RELEASE) {
seat_feed_keyboard_key(seat, data->keys->code[i], false);
}
}
}
static void handle_input_action(struct input_action *input_action)
{
if (!input_action) {
return;
}
struct action_data *action_data = input_action->action;
switch (action_data->type) {
case ACTION_TYPE_DBUS_ACTION:
action_call_dbus_method(&action_data->data.dbus);
break;
case ACTION_TYPE_RUN_COMMAND:
spawn_invoke(action_data->data.command.cmd);
break;
case ACTION_TYPE_SEND_BUTTON:
action_call_send_button(&action_data->data.button);
break;
case ACTION_TYPE_SEND_KEY:
action_call_send_key(&action_data->data.key);
break;
default:
break;
}
kywc_log(KYWC_DEBUG, "Input_action: %s, type: %d", input_action->bindings, action_data->type);
}
static void input_manager_keybinding_action(struct key_binding *binding, void *data)
{
handle_input_action(data);
}
static void input_manager_gesturebinding_action(struct gesture_binding *binding, void *data,
double dx, double dy)
{
handle_input_action(data);
}
static void input_action_destroy(struct input_action *input_action)
{
if (input_action->action->type == ACTION_TYPE_DBUS_ACTION) {
free(input_action->action->data.dbus.service);
free(input_action->action->data.dbus.path);
free(input_action->action->data.dbus.interface);
free(input_action->action->data.dbus.method);
free(input_action->action->data.dbus.param_type);
free(input_action->action->data.dbus.param_value);
} else if (input_action->action->type == ACTION_TYPE_RUN_COMMAND) {
free(input_action->action->data.command.cmd);
} else if (input_action->action->type == ACTION_TYPE_SEND_KEY) {
struct keycodes *modifiers = input_action->action->data.key.modifiers;
struct keycodes *keys = input_action->action->data.key.keys;
if (modifiers) {
free(modifiers->code);
}
if (keys) {
free(keys->code);
}
free(modifiers);
free(keys);
}
wl_list_remove(&input_action->link);
free(input_action->bindings);
free(input_action->action);
free(input_action);
}
static bool input_action_manager_delete_config(struct input_action_manager *manager,
struct input_action *input_action)
{
if (!manager->config || !manager->config->json || !input_action) {
return false;
}
char *input_type = input_action->type == INPUT_TYPE_KEYBOARD ? "keyboard" : "gesture";
json_object *config = json_object_object_get(manager->config->json, input_type);
if (!config) {
return false;
}
json_object_object_del(config, input_action->bindings);
return true;
}
static bool input_action_manager_write_config(struct input_action_manager *manager,
struct input_action *input_action)
{
if (!manager->config || !manager->config->json || !input_action) {
return false;
}
char *input_type = input_action->type == INPUT_TYPE_KEYBOARD ? "keyboard" : "gesture";
json_object *config = json_object_object_get(manager->config->json, input_type);
if (!config) {
config = json_object_new_object();
json_object_object_add(manager->config->json, input_type, config);
}
json_object *action_config = json_object_object_get(config, input_action->bindings);
if (!action_config) {
action_config = json_object_new_object();
json_object_object_add(config, input_action->bindings, action_config);
}
struct action_data *action_data = input_action->action;
json_object_object_add(action_config, "enable", json_object_new_boolean(action_data->enable));
switch (action_data->type) {
case ACTION_TYPE_DBUS_ACTION:
json_object_object_add(action_config, "actiontype", json_object_new_string("dbus"));
const char *bustype =
action_data->data.dbus.type == DBUS_TYPE_SESSION ? "session" : "system";
json_object_object_add(action_config, "bustype", json_object_new_string(bustype));
json_object_object_add(action_config, "service",
json_object_new_string(action_data->data.dbus.service));
json_object_object_add(action_config, "path",
json_object_new_string(action_data->data.dbus.path));
json_object_object_add(action_config, "interface",
json_object_new_string(action_data->data.dbus.interface));
json_object_object_add(action_config, "method",
json_object_new_string(action_data->data.dbus.method));
break;
case ACTION_TYPE_RUN_COMMAND:
json_object_object_add(action_config, "actiontype", json_object_new_string("command"));
json_object_object_add(action_config, "command",
json_object_new_string(action_data->data.command.cmd));
break;
case ACTION_TYPE_SEND_BUTTON:
json_object_object_add(action_config, "actiontype", json_object_new_string("button"));
const char *btnstr = btncode_to_str(action_data->data.button.val);
if (btnstr) {
json_object_object_add(action_config, "button", json_object_new_string(btnstr));
}
break;
case ACTION_TYPE_SEND_KEY:
json_object_object_add(action_config, "actiontype", json_object_new_string("key"));
char *keystr = keycodes_to_str(action_data->data.key.modifiers);
if (keystr) {
json_object_object_add(action_config, "modifiers", json_object_new_string(keystr));
free(keystr);
}
keystr = keycodes_to_str(action_data->data.key.keys);
if (keystr) {
json_object_object_add(action_config, "keys", json_object_new_string(keystr));
free(keystr);
}
break;
default:
break;
}
if (action_data->desc) {
json_object_object_add(action_config, "desc", json_object_new_string(action_data->desc));
}
return true;
}
static struct action_data *action_data_create_from_config(json_object *action_config)
{
struct action_data *action_data = calloc(1, sizeof(*action_data));
if (!action_data) {
return NULL;
}
json_object *data;
if (json_object_object_get_ex(action_config, "desc", &data)) {
action_data->desc = json_object_get_string(data);
}
enum action_type type = ACTION_TYPE_NONE;
if (json_object_object_get_ex(action_config, "actiontype", &data)) {
const char *type_str = json_object_get_string(data);
if (strcmp(type_str, "dbus") == 0) {
type = ACTION_TYPE_DBUS_ACTION;
} else if (strcmp(type_str, "command") == 0) {
type = ACTION_TYPE_RUN_COMMAND;
} else if (strcmp(type_str, "button") == 0) {
type = ACTION_TYPE_SEND_BUTTON;
} else if (strcmp(type_str, "key") == 0) {
type = ACTION_TYPE_SEND_KEY;
}
action_data->type = type;
}
if (json_object_object_get_ex(action_config, "type", &data)) {
const char *type_str = json_object_get_string(data);
action_data->binding_type = kywc_key_binding_type_by_name(type_str, NULL);
}
switch (type) {
case ACTION_TYPE_DBUS_ACTION:
if (json_object_object_get_ex(action_config, "bustype", &data)) {
const char *bustype = json_object_get_string(data);
if (strcmp(bustype, "session") == 0) {
action_data->data.dbus.type = DBUS_TYPE_SESSION;
} else {
action_data->data.dbus.type = DBUS_TYPE_SYSTEM;
}
}
if (json_object_object_get_ex(action_config, "service", &data)) {
action_data->data.dbus.service = strdup(json_object_get_string(data));
}
if (json_object_object_get_ex(action_config, "path", &data)) {
action_data->data.dbus.path = strdup(json_object_get_string(data));
}
if (json_object_object_get_ex(action_config, "interface", &data)) {
action_data->data.dbus.interface = strdup(json_object_get_string(data));
}
if (json_object_object_get_ex(action_config, "method", &data)) {
action_data->data.dbus.method = strdup(json_object_get_string(data));
}
if (json_object_object_get_ex(action_config, "param_type", &data)) {
action_data->data.dbus.param_type = strdup(json_object_get_string(data));
}
if (json_object_object_get_ex(action_config, "param_value", &data)) {
action_data->data.dbus.param_value = strdup(json_object_get_string(data));
}
break;
case ACTION_TYPE_RUN_COMMAND:
if (json_object_object_get_ex(action_config, "command", &data)) {
action_data->data.command.cmd = strdup(json_object_get_string(data));
}
break;
case ACTION_TYPE_SEND_BUTTON:
if (json_object_object_get_ex(action_config, "button", &data)) {
const char *button = json_object_get_string(data);
action_data->data.button.val = btncode_map(button);
}
break;
case ACTION_TYPE_SEND_KEY:
if (json_object_object_get_ex(action_config, "modifiers", &data)) {
const char *modifiers = json_object_get_string(data);
action_data->data.key.modifiers = keycodes_create(modifiers);
}
if (json_object_object_get_ex(action_config, "keys", &data)) {
const char *keys = json_object_get_string(data);
action_data->data.key.keys = keycodes_create(keys);
}
break;
default:
break;
}
if (json_object_object_get_ex(action_config, "enable", &data)) {
action_data->enable = json_object_get_boolean(data);
}
return action_data;
}
static int add_input_action(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
struct input_action_manager *manager = userdata;
const char *input_type = NULL, *input_bindings = NULL;
const char *action_desc = NULL, *action_dat = NULL;
const char *binding_type = NULL;
CK(sd_bus_message_read(m, "sssss", &input_type, &input_bindings, &action_desc, &action_dat,
&binding_type));
enum input_type itype;
if (strcmp(input_type, "keyboard") == 0) {
itype = INPUT_TYPE_KEYBOARD;
} else if (strcmp(input_type, "gesture") == 0) {
itype = INPUT_TYPE_GESTURE;
} else {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input_type.");
return sd_bus_reply_method_error(m, &error);
}
void *binding =
itype == INPUT_TYPE_KEYBOARD
? (void *)kywc_key_binding_create(input_bindings, action_desc)
: (void *)kywc_gesture_binding_create_by_string(input_bindings, action_desc);
if (!binding) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input_bindings.");
return sd_bus_reply_method_error(m, &error);
}
struct action_data *action_data = action_data_create_from_bus_string(action_dat);
if (!action_data) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid action_data.");
return sd_bus_reply_method_error(m, &error);
}
struct input_action *input_action = calloc(1, sizeof(*input_action));
input_action->type = itype;
input_action->bindings = strdup(input_bindings);
input_action->action = action_data;
action_data->desc = strdup(action_desc);
if (itype == INPUT_TYPE_KEYBOARD) {
action_data->binding_type = kywc_key_binding_type_by_name(binding_type, NULL);
}
if (action_data->enable) {
bool ret = itype == INPUT_TYPE_KEYBOARD
? kywc_key_binding_register(binding, action_data->binding_type,
input_manager_keybinding_action, input_action)
: kywc_gesture_binding_register(binding, input_manager_gesturebinding_action,
input_action);
if (!ret) {
itype == INPUT_TYPE_KEYBOARD ? kywc_key_binding_destroy(binding)
: kywc_gesture_binding_destroy(binding);
wl_list_init(&input_action->link);
input_action_destroy(input_action);
const sd_bus_error error = SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS,
"Failed register input_bindings.");
return sd_bus_reply_method_error(m, &error);
}
}
input_action->data = binding;
struct input_action *old, *temp;
wl_list_for_each_safe(old, temp, &manager->actions, link) {
if (strcmp(old->bindings, input_action->bindings) == 0) {
input_action_destroy(old);
}
}
input_action_manager_write_config(manager, input_action);
wl_list_insert(&manager->actions, &input_action->link);
return sd_bus_reply_method_return(m, NULL);
}
static int control_input_action(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
struct input_action_manager *manager = userdata;
const char *control_type = NULL, *input_bindings = NULL;
CK(sd_bus_message_read(m, "ss", &control_type, &input_bindings));
enum control_type ctype;
if (strcmp(control_type, "delete") == 0) {
ctype = CONTROL_TYPE_DELETE;
} else if (strcmp(control_type, "disable") == 0) {
ctype = CONTROL_TYPE_DISABLE;
} else if (strcmp(control_type, "enable") == 0) {
ctype = CONTROL_TYPE_ENABLE;
} else {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid control_type.");
return sd_bus_reply_method_error(m, &error);
}
bool found = false;
struct input_action *input_action = NULL;
wl_list_for_each(input_action, &manager->actions, link) {
if (strcmp(input_bindings, input_action->bindings)) {
continue;
}
found = true;
break;
}
if (!found) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input_bindings.");
return sd_bus_reply_method_error(m, &error);
}
switch (ctype) {
case CONTROL_TYPE_DELETE:
if (input_action->type == INPUT_TYPE_KEYBOARD && input_action->data) {
kywc_key_binding_destroy(input_action->data);
} else if (input_action->type == INPUT_TYPE_GESTURE && input_action->data) {
kywc_gesture_binding_destroy(input_action->data);
}
input_action_manager_delete_config(manager, input_action);
input_action_destroy(input_action);
break;
case CONTROL_TYPE_DISABLE:
if (!input_action->action->enable) {
break;
}
if (input_action->type == INPUT_TYPE_KEYBOARD && input_action->data) {
kywc_key_binding_destroy(input_action->data);
} else if (input_action->type == INPUT_TYPE_GESTURE && input_action->data) {
kywc_gesture_binding_destroy(input_action->data);
}
input_action->data = NULL;
input_action->action->enable = false;
input_action_manager_write_config(manager, input_action);
break;
case CONTROL_TYPE_ENABLE:
if (input_action->action->enable) {
break;
}
if (input_action->type == INPUT_TYPE_KEYBOARD) {
if (!input_action->data) {
input_action->data =
kywc_key_binding_create(input_action->bindings, input_action->action->desc);
}
if (input_action->data) {
if (!kywc_key_binding_register(input_action->data,
input_action->action->binding_type,
input_manager_keybinding_action, input_action)) {
kywc_key_binding_destroy(input_action->data);
input_action->data = NULL;
}
}
} else if (input_action->type == INPUT_TYPE_GESTURE) {
if (!input_action->data) {
input_action->data = kywc_gesture_binding_create_by_string(
input_action->bindings, input_action->action->desc);
}
if (input_action->data) {
if (!kywc_gesture_binding_register(
input_action->data, input_manager_gesturebinding_action, input_action)) {
kywc_key_binding_destroy(input_action->data);
input_action->data = NULL;
}
}
}
input_action->action->enable = true;
input_action_manager_write_config(manager, input_action);
break;
default:
break;
}
return sd_bus_reply_method_return(m, NULL);
}
static const sd_bus_vtable service_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("ListAllActions", "", "a(ss)", list_input_actions, 0),
SD_BUS_METHOD("AddAction", "sssss", "", add_input_action, 0),
SD_BUS_METHOD("ControlAction", "ss", "", control_input_action, 0),
SD_BUS_VTABLE_END,
};
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct input_action_manager *manager = wl_container_of(listener, manager, server_destroy);
struct input_action *action, *temp;
wl_list_for_each_safe(action, temp, &manager->actions, link) {
input_action_destroy(action);
}
wl_list_remove(&manager->server_destroy.link);
wl_list_remove(&manager->actions);
free(manager);
}
static void input_action_create_with_keyboard(struct input_action_manager *manager,
json_object *keyboard_obj, bool user)
{
json_object_object_foreach(keyboard_obj, keybind, action_config) {
struct action_data *action_data = action_data_create_from_config(action_config);
if (!action_data) {
continue;
}
struct input_action *input_action = calloc(1, sizeof(*input_action));
input_action->bindings = strdup(keybind);
input_action->type = INPUT_TYPE_KEYBOARD;
input_action->action = action_data;
kywc_log(KYWC_DEBUG, "Input_action keybind: %s", keybind);
wl_list_insert(&manager->actions, &input_action->link);
if (!action_data->enable) {
continue;
}
struct key_binding *binding = kywc_key_binding_create(keybind, action_data->desc);
if (!binding) {
continue;
}
if (!kywc_key_binding_register(binding, action_data->binding_type,
input_manager_keybinding_action, input_action)) {
kywc_log(KYWC_DEBUG, "Key_binding registion failed");
if (user) {
json_object_object_del(keyboard_obj, keybind);
}
kywc_key_binding_destroy(binding);
binding = NULL;
}
input_action->data = binding;
}
}
static void input_action_create_with_gesture(struct input_action_manager *manager,
json_object *gesture_obj, bool user)
{
json_object_object_foreach(gesture_obj, gesture, action_config) {
struct action_data *action_data = action_data_create_from_config(action_config);
if (!action_data) {
continue;
}
struct input_action *input_action = calloc(1, sizeof(*input_action));
input_action->bindings = strdup(gesture);
input_action->type = INPUT_TYPE_GESTURE;
input_action->action = action_data;
kywc_log(KYWC_DEBUG, "Input_action gesture bind: %s", gesture);
wl_list_insert(&manager->actions, &input_action->link);
if (!action_data->enable) {
continue;
}
struct gesture_binding *binding =
kywc_gesture_binding_create_by_string(gesture, action_data->desc);
if (!binding) {
continue;
}
if (!kywc_gesture_binding_register(binding, input_manager_gesturebinding_action,
input_action)) {
kywc_log(KYWC_DEBUG, "Gesture_binding registion failed");
if (user) {
json_object_object_del(gesture_obj, gesture);
}
kywc_gesture_binding_destroy(binding);
binding = NULL;
}
input_action->data = binding;
}
}
static bool input_action_manager_read_config(struct input_action_manager *manager)
{
if (!manager->config || !manager->config->json) {
return false;
}
json_object *data;
/* get system default config */
if (manager->config->sys_json &&
json_object_object_get_ex(manager->config->sys_json, "keyboard", &data)) {
input_action_create_with_keyboard(manager, data, false);
}
if (manager->config->sys_json &&
json_object_object_get_ex(manager->config->sys_json, "gesture", &data)) {
input_action_create_with_gesture(manager, data, false);
}
/* get user config */
if (manager->config->json &&
json_object_object_get_ex(manager->config->json, "keyboard", &data)) {
input_action_create_with_keyboard(manager, data, true);
}
if (json_object_object_get_ex(manager->config->json, "gesture", &data)) {
input_action_create_with_gesture(manager, data, true);
}
return true;
}
static bool input_action_manager_config_init(struct input_action_manager *manager)
{
manager->config = config_manager_add_config("InputAction");
if (!manager->config) {
return false;
}
return dbus_register_object(NULL, service_path, service_interface, service_vtable, manager);
}
bool input_action_manager_create(struct server *server)
{
manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->server = server;
wl_list_init(&manager->actions);
input_action_manager_config_init(manager);
input_action_manager_read_config(manager);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &manager->server_destroy);
return true;
}
kylin-wayland-compositor/src/input/idle.c 0000664 0001750 0001750 00000027505 15160461067 017505 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include "ext-idle-notify-v1-protocol.h"
#include "idle-protocol.h"
#include "input/seat.h"
#include "input_p.h"
#include "server.h"
#define IDLE_NOTIFIER_VERSION 1
#define ORG_KDE_KWIN_IDLE_VERSION 1
struct idle_manager {
struct wl_display *display;
struct wl_global *kde_idle_global;
struct wl_global *idle_notifier_global;
struct wl_list idles;
bool inhibited;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
enum idle_type {
IDLE_FROM_KDE_IDLE,
IDLE_FROM_IDLE_NOTIFIER,
IDLE_FROM_SERVER,
};
struct idle {
enum idle_type type;
struct wl_list link;
uint32_t timeout_ms;
struct wl_event_source *timer;
bool idle_state;
bool support_inhibit;
struct seat *seat;
struct wl_listener seat_destroy;
void (*idle_func)(struct idle *idle, void *data);
void (*resume_func)(struct idle *idle, void *data);
void (*destroy_func)(struct idle *idle, void *data);
void *data; // resource
};
static struct idle_manager *idle_manager = NULL;
static void idle_set_idle(struct idle *idle, bool idle_state)
{
if (idle->idle_state == idle_state) {
return;
}
if (idle_state) {
idle->idle_func(idle, idle->data);
} else {
idle->resume_func(idle, idle->data);
}
idle->idle_state = idle_state;
}
static void idle_notifier_resource_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static int idle_handle_timer(void *data)
{
struct idle *idle = data;
idle_set_idle(idle, true);
return 0;
}
void idle_destroy(struct idle *idle)
{
if (!idle) {
return;
}
if (idle->destroy_func) {
idle->destroy_func(idle, idle->data);
}
wl_list_remove(&idle->link);
wl_list_remove(&idle->seat_destroy.link);
if (idle->timer) {
wl_event_source_remove(idle->timer);
}
if (idle->type != IDLE_FROM_SERVER) {
wl_resource_set_user_data(idle->data, NULL);
}
free(idle);
}
static void idle_reset_timer(struct idle *idle)
{
if (idle_manager->inhibited && idle->support_inhibit) {
idle_set_idle(idle, false);
if (idle->timer) {
wl_event_source_timer_update(idle->timer, 0);
}
return;
}
if (idle->timer) {
wl_event_source_timer_update(idle->timer, idle->timeout_ms);
} else {
idle_set_idle(idle, true);
}
}
static void idle_handle_seat_destroy(struct wl_listener *listener, void *data)
{
struct idle *idle = wl_container_of(listener, idle, seat_destroy);
idle_destroy(idle);
}
static struct idle *idle_create(enum idle_type type, struct seat *seat, bool support_inhibit,
uint32_t timeout, void (*idle_func)(struct idle *idle, void *data),
void (*resume_func)(struct idle *idle, void *data),
void (*destroy_func)(struct idle *idle, void *data), void *data)
{
struct idle *idle = calloc(1, sizeof(*idle));
if (!idle) {
return NULL;
}
idle->type = type;
idle->timeout_ms = timeout;
idle->support_inhibit = support_inhibit;
idle->seat = seat;
idle->idle_func = idle_func;
idle->resume_func = resume_func;
idle->destroy_func = destroy_func;
idle->data = data;
if (timeout > 0) {
struct wl_event_loop *loop = wl_display_get_event_loop(idle_manager->display);
idle->timer = wl_event_loop_add_timer(loop, idle_handle_timer, idle);
if (!idle->timer) {
free(idle);
return NULL;
}
}
wl_list_insert(&idle_manager->idles, &idle->link);
idle->seat_destroy.notify = idle_handle_seat_destroy;
wl_signal_add(&seat->events.destroy, &idle->seat_destroy);
return idle;
}
static void idle_notification_destroy(struct wl_resource *resource)
{
struct idle *idle = wl_resource_get_user_data(resource);
idle_destroy(idle);
}
static const struct ext_idle_notification_v1_interface notification_impl = {
.destroy = idle_notifier_resource_destroy,
};
static void idle_notification_idle(struct idle *idle, void *data)
{
struct wl_resource *resource = data;
ext_idle_notification_v1_send_idled(resource);
}
static void idle_notification_resume(struct idle *idle, void *data)
{
struct wl_resource *resource = data;
ext_idle_notification_v1_send_resumed(resource);
}
static void idle_notifier_get_idle_notification(struct wl_client *client,
struct wl_resource *notifier_resource, uint32_t id,
uint32_t timeout, struct wl_resource *seat_resource)
{
uint32_t version = wl_resource_get_version(notifier_resource);
struct wl_resource *resource =
wl_resource_create(client, &ext_idle_notification_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, ¬ification_impl, NULL, idle_notification_destroy);
struct seat *seat = seat_from_resource(seat_resource);
if (!seat) {
return; // leave the resource inert
}
struct idle *idle =
idle_create(IDLE_FROM_IDLE_NOTIFIER, seat, true, timeout, idle_notification_idle,
idle_notification_resume, NULL, resource);
if (!idle) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_user_data(resource, idle);
idle_reset_timer(idle);
}
static const struct ext_idle_notifier_v1_interface idle_notifier_impl = {
.destroy = idle_notifier_resource_destroy,
.get_idle_notification = idle_notifier_get_idle_notification,
};
static void idle_notifier_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &ext_idle_notifier_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &idle_notifier_impl, NULL, NULL);
}
static void idle_timeout_destroy(struct wl_resource *resource)
{
struct idle *idle = wl_resource_get_user_data(resource);
idle_destroy(idle);
}
static void idle_timeout_release(struct wl_client *client, struct wl_resource *resource)
{
idle_timeout_destroy(resource);
}
static void simulate_activity(struct wl_client *client, struct wl_resource *resource)
{
struct idle *idle = wl_resource_get_user_data(resource);
if (idle->idle_state) {
idle->idle_state = false;
org_kde_kwin_idle_timeout_send_resumed(idle->data);
}
if (idle->timer) {
wl_event_source_timer_update(idle->timer, idle->timeout_ms);
} else {
idle_set_idle(idle, true);
}
}
static const struct org_kde_kwin_idle_timeout_interface idle_timeout_impl = {
.release = idle_timeout_release,
.simulate_user_activity = simulate_activity,
};
static void kde_idle_idle(struct idle *idle, void *data)
{
struct wl_resource *resource = data;
org_kde_kwin_idle_timeout_send_idle(resource);
}
static void kde_idle_resume(struct idle *idle, void *data)
{
struct wl_resource *resource = data;
org_kde_kwin_idle_timeout_send_resumed(resource);
}
static void kde_idle_get_idle_timeout(struct wl_client *client, struct wl_resource *idle_resource,
uint32_t id, struct wl_resource *seat_resource,
uint32_t timeout)
{
uint32_t version = wl_resource_get_version(idle_resource);
struct wl_resource *resource =
wl_resource_create(client, &org_kde_kwin_idle_timeout_interface, version, id);
if (!resource) {
wl_resource_post_no_memory(idle_resource);
return;
}
wl_resource_set_implementation(resource, &idle_timeout_impl, NULL, idle_timeout_destroy);
struct seat *seat = seat_from_resource(seat_resource);
if (!seat) {
return;
}
struct idle *idle = idle_create(IDLE_FROM_KDE_IDLE, seat, true, timeout, kde_idle_idle,
kde_idle_resume, NULL, resource);
if (!idle) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_user_data(resource, idle);
idle_reset_timer(idle);
}
static const struct org_kde_kwin_idle_interface kde_idle_impl = {
.get_idle_timeout = kde_idle_get_idle_timeout,
};
static void kde_idle_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &org_kde_kwin_idle_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &kde_idle_impl, NULL, NULL);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&idle_manager->display_destroy.link);
if (idle_manager->idle_notifier_global) {
wl_global_destroy(idle_manager->idle_notifier_global);
}
if (idle_manager->kde_idle_global) {
wl_global_destroy(idle_manager->kde_idle_global);
}
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&idle_manager->server_destroy.link);
free(idle_manager);
}
bool idle_manager_create(struct server *server)
{
idle_manager = calloc(1, sizeof(*idle_manager));
if (!idle_manager) {
return false;
}
idle_manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &idle_manager->display_destroy);
idle_manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &idle_manager->server_destroy);
idle_manager->idle_notifier_global =
wl_global_create(server->display, &ext_idle_notifier_v1_interface, IDLE_NOTIFIER_VERSION,
idle_manager, idle_notifier_bind);
if (!idle_manager->idle_notifier_global) {
kywc_log(KYWC_WARN, "Failed to create %s global", ext_idle_notifier_v1_interface.name);
}
idle_manager->idle_notifier_global =
wl_global_create(server->display, &org_kde_kwin_idle_interface, ORG_KDE_KWIN_IDLE_VERSION,
idle_manager, kde_idle_bind);
if (!idle_manager->idle_notifier_global) {
kywc_log(KYWC_WARN, "Failed to create %s global", org_kde_kwin_idle_interface.name);
}
idle_manager->display = server->display;
wl_list_init(&idle_manager->idles);
return true;
}
void idle_manager_set_inhibited(bool inhibited)
{
if (idle_manager->inhibited == inhibited) {
return;
}
idle_manager->inhibited = inhibited;
struct idle *idle;
wl_list_for_each(idle, &idle_manager->idles, link) {
if (idle->support_inhibit) {
idle_reset_timer(idle);
}
}
}
void idle_manager_notify_activity(struct seat *seat)
{
struct idle *idle;
wl_list_for_each(idle, &idle_manager->idles, link) {
if (idle->support_inhibit && idle_manager->inhibited) {
continue;
}
if (idle->seat == seat) {
idle_set_idle(idle, false);
idle_reset_timer(idle);
}
}
}
struct idle *idle_manager_add_idle(struct seat *seat, bool support_inhibit, uint32_t timeout,
void (*idle_func)(struct idle *idle, void *data),
void (*resume_func)(struct idle *idle, void *data),
void (*destroy_func)(struct idle *idle, void *data), void *data)
{
struct idle *idle = idle_create(IDLE_FROM_SERVER, seat, support_inhibit, timeout, idle_func,
resume_func, destroy_func, data);
if (!idle) {
return NULL;
}
idle_reset_timer(idle);
return idle;
}
kylin-wayland-compositor/src/input/keyboard_group.c 0000664 0001750 0001750 00000024255 15160460057 021601 0 ustar feng feng // SPDX-FileCopyrightText: 2023 The wlroots contributors
// SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include "input/keyboard.h"
#include "input/keyboard_group.h"
struct keyboard_group_device {
struct wlr_keyboard *keyboard;
struct wl_listener key;
struct wl_listener modifiers;
struct wl_listener keymap;
struct wl_listener repeat_info;
struct wl_listener destroy;
struct wl_list link; // keyboard_group.devices
};
struct keyboard_group_key {
uint32_t keycode;
size_t count;
struct wl_list link; // keyboard_group.keys
};
static void keyboard_group_set_leds(struct wlr_keyboard *kb, uint32_t leds)
{
struct keyboard_group *group = keyboard_group_from_wlr_keyboard(kb);
uint32_t fixed_leds = leds;
if (group->scroll_lock > 0) {
fixed_leds |= WLR_LED_SCROLL_LOCK;
} else {
fixed_leds &= ~WLR_LED_SCROLL_LOCK;
}
struct keyboard_group_device *device;
wl_list_for_each(device, &group->devices, link) {
if (group->scroll_lock_led_on) {
device->keyboard->leds |= WLR_LED_SCROLL_LOCK;
}
wlr_keyboard_led_update(device->keyboard, fixed_leds);
device->keyboard->leds &= ~WLR_LED_SCROLL_LOCK;
}
group->scroll_lock_led_on = group->scroll_lock > 0;
}
static const struct wlr_keyboard_impl impl = {
.name = "keyboard-group",
.led_update = keyboard_group_set_leds,
};
struct keyboard_group *keyboard_group_create(void)
{
struct keyboard_group *group = calloc(1, sizeof(*group));
if (!group) {
kywc_log(KYWC_ERROR, "Failed to allocate keyboard_group");
return NULL;
}
wlr_keyboard_init(&group->keyboard, &impl, "keyboard_group");
wl_list_init(&group->devices);
wl_list_init(&group->keys);
return group;
}
struct keyboard_group *keyboard_group_from_wlr_keyboard(struct wlr_keyboard *keyboard)
{
if (keyboard->impl != &impl) {
return NULL;
}
struct keyboard_group *group = wl_container_of(keyboard, group, keyboard);
return group;
}
static bool process_key(struct keyboard_group_device *group_device,
struct wlr_keyboard_key_event *event)
{
struct keyboard_group *group = (struct keyboard_group *)group_device->keyboard->group;
struct keyboard_group_key *key, *tmp;
wl_list_for_each_safe(key, tmp, &group->keys, link) {
if (key->keycode != event->keycode) {
continue;
}
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
key->count++;
return false;
}
if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) {
key->count--;
if (key->count > 0) {
return false;
}
wl_list_remove(&key->link);
free(key);
}
break;
}
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
struct keyboard_group_key *key = calloc(1, sizeof(*key));
if (!key) {
kywc_log(KYWC_ERROR, "Failed to allocate keyboard_group_key");
return false;
}
key->keycode = event->keycode;
key->count = 1;
wl_list_insert(&group->keys, &key->link);
}
return true;
}
static void handle_keyboard_key(struct wl_listener *listener, void *data)
{
struct keyboard_group_device *group_device = wl_container_of(listener, group_device, key);
struct keyboard_group *group = (struct keyboard_group *)group_device->keyboard->group;
if (process_key(group_device, data)) {
wlr_keyboard_notify_key(&group->keyboard, data);
}
}
static void handle_keyboard_modifiers(struct wl_listener *listener, void *data)
{
// Sync the effective layout (group modifier) to all keyboards. The rest of
// the modifiers will be derived from the keyboard_group's key state
struct keyboard_group_device *group_device = wl_container_of(listener, group_device, modifiers);
struct keyboard_group *group = (struct keyboard_group *)group_device->keyboard->group;
struct wlr_keyboard_modifiers mods = group_device->keyboard->modifiers;
struct keyboard_group_device *device;
wl_list_for_each(device, &group->devices, link) {
if (mods.depressed != device->keyboard->modifiers.depressed ||
mods.latched != device->keyboard->modifiers.latched ||
mods.locked != device->keyboard->modifiers.locked ||
mods.group != device->keyboard->modifiers.group) {
wlr_keyboard_notify_modifiers(device->keyboard, mods.depressed, mods.latched,
mods.locked, mods.group);
return;
}
}
wlr_keyboard_notify_modifiers(&group->keyboard, mods.depressed, mods.latched, mods.locked,
mods.group);
}
static void handle_keyboard_keymap(struct wl_listener *listener, void *data)
{
struct keyboard_group_device *group_device = wl_container_of(listener, group_device, keymap);
struct wlr_keyboard *keyboard = group_device->keyboard;
struct keyboard_group *group = (struct keyboard_group *)keyboard->group;
if (!keyboard_keymaps_match(&group->keyboard, keyboard)) {
struct keyboard_group_device *device;
wl_list_for_each(device, &group->devices, link) {
if (!keyboard_keymaps_match(keyboard, device->keyboard)) {
wlr_keyboard_set_keymap(device->keyboard, keyboard->keymap);
return;
}
}
}
wlr_keyboard_set_keymap(&group->keyboard, keyboard->keymap);
}
static void handle_keyboard_repeat_info(struct wl_listener *listener, void *data)
{
struct keyboard_group_device *group_device =
wl_container_of(listener, group_device, repeat_info);
struct wlr_keyboard *keyboard = group_device->keyboard;
struct keyboard_group *group = (struct keyboard_group *)keyboard->group;
struct keyboard_group_device *device;
wl_list_for_each(device, &group->devices, link) {
struct wlr_keyboard *devkb = device->keyboard;
if (devkb->repeat_info.rate != keyboard->repeat_info.rate ||
devkb->repeat_info.delay != keyboard->repeat_info.delay) {
wlr_keyboard_set_repeat_info(devkb, keyboard->repeat_info.rate,
keyboard->repeat_info.delay);
return;
}
}
wlr_keyboard_set_repeat_info(&group->keyboard, keyboard->repeat_info.rate,
keyboard->repeat_info.delay);
}
static void remove_keyboard_group_device(struct keyboard_group_device *device)
{
device->keyboard->group = NULL;
wl_list_remove(&device->link);
wl_list_remove(&device->key.link);
wl_list_remove(&device->modifiers.link);
wl_list_remove(&device->keymap.link);
wl_list_remove(&device->repeat_info.link);
wl_list_remove(&device->destroy.link);
free(device);
}
static void handle_keyboard_destroy(struct wl_listener *listener, void *data)
{
struct keyboard_group_device *device = wl_container_of(listener, device, destroy);
remove_keyboard_group_device(device);
}
bool keyboard_group_add_keyboard(struct keyboard_group *group, struct wlr_keyboard *keyboard)
{
if (keyboard->group) {
kywc_log(KYWC_ERROR, "A wlr_keyboard can only belong to one group");
return false;
}
if (keyboard->impl == &impl) {
kywc_log(KYWC_ERROR, "Cannot add a group's keyboard to a group");
return false;
}
if (!keyboard_keymaps_match(&group->keyboard, keyboard)) {
kywc_log(KYWC_ERROR, "Device keymap does not match keyboard group's");
return false;
}
struct keyboard_group_device *device = calloc(1, sizeof(*device));
if (!device) {
kywc_log(KYWC_ERROR, "Failed to allocate keyboard_group_device");
return false;
}
device->keyboard = keyboard;
keyboard->group = (struct wlr_keyboard_group *)group;
wl_list_insert(&group->devices, &device->link);
wl_signal_add(&keyboard->events.key, &device->key);
device->key.notify = handle_keyboard_key;
wl_signal_add(&keyboard->events.modifiers, &device->modifiers);
device->modifiers.notify = handle_keyboard_modifiers;
wl_signal_add(&keyboard->events.keymap, &device->keymap);
device->keymap.notify = handle_keyboard_keymap;
wl_signal_add(&keyboard->events.repeat_info, &device->repeat_info);
device->repeat_info.notify = handle_keyboard_repeat_info;
wl_signal_add(&keyboard->base.events.destroy, &device->destroy);
device->destroy.notify = handle_keyboard_destroy;
struct wlr_keyboard *group_kb = &group->keyboard;
wlr_keyboard_set_repeat_info(keyboard, group_kb->repeat_info.rate, group_kb->repeat_info.delay);
/* sync the group modifiers to keyboard */
wlr_keyboard_notify_modifiers(keyboard, group_kb->modifiers.depressed,
group_kb->modifiers.latched, group_kb->modifiers.locked,
group_kb->modifiers.group);
/* force sync leds to keyboard */
group->scroll_lock_led_on = false;
keyboard_group_set_leds(group_kb, group_kb->leds);
return true;
}
void keyboard_group_remove_keyboard(struct keyboard_group *group, struct wlr_keyboard *keyboard)
{
struct keyboard_group_device *device, *tmp;
wl_list_for_each_safe(device, tmp, &group->devices, link) {
if (device->keyboard == keyboard) {
remove_keyboard_group_device(device);
return;
}
}
kywc_log(KYWC_ERROR, "keyboard not found in group");
}
void keyboard_group_destroy(struct keyboard_group *group)
{
struct keyboard_group_device *device, *tmp;
wl_list_for_each_safe(device, tmp, &group->devices, link) {
keyboard_group_remove_keyboard(group, device->keyboard);
}
wlr_keyboard_finish(&group->keyboard);
free(group);
}
struct wlr_keyboard *keyboard_group_pick_keyboard(struct keyboard_group *group)
{
if (wl_list_empty(&group->devices)) {
return &group->keyboard;
}
struct keyboard_group_device *device = wl_container_of(group->devices.next, device, link);
return device->keyboard;
}
kylin-wayland-compositor/src/input/transient_seat.c 0000664 0001750 0001750 00000012210 15160460057 021574 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include "ext-transient-seat-v1-protocol.h"
#include "input_p.h"
#include "server.h"
struct transient_seat_manager {
struct wl_global *global;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
struct input_manager *input_manager;
};
struct transient_seat {
struct wl_resource *resource;
struct seat *seat;
struct wl_listener seat_destroy;
};
static void transient_seat_destroy(struct transient_seat *seat)
{
wl_list_remove(&seat->seat_destroy.link);
if (seat->seat) {
seat_destroy(seat->seat);
}
free(seat);
}
static void transient_seat_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct ext_transient_seat_v1_interface transient_seat_impl = {
.destroy = transient_seat_handle_destroy,
};
static void transient_seat_handle_resource_destroy(struct wl_resource *resource)
{
struct transient_seat *seat = wl_resource_get_user_data(resource);
if (seat) {
transient_seat_destroy(seat);
}
}
static void transient_seat_handle_seat_destroy(struct wl_listener *listener, void *data)
{
struct transient_seat *seat = wl_container_of(listener, seat, seat_destroy);
wl_resource_set_user_data(seat->resource, NULL);
seat->seat = NULL;
transient_seat_destroy(seat);
}
static void manager_handle_create(struct wl_client *client, struct wl_resource *manager_resource,
uint32_t id)
{
struct transient_seat *seat = calloc(1, sizeof(*seat));
if (!seat) {
wl_client_post_no_memory(client);
return;
}
int version = wl_resource_get_version(manager_resource);
seat->resource = wl_resource_create(client, &ext_transient_seat_v1_interface, version, id);
if (!seat->resource) {
free(seat);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(seat->resource, &transient_seat_impl, seat,
transient_seat_handle_resource_destroy);
// create seat and send ready or denied
static uint64_t i = 0;
char name[256];
snprintf(name, sizeof(name), "transient-%" PRIx64, i++);
struct transient_seat_manager *manager = wl_resource_get_user_data(manager_resource);
seat->seat = seat_create(manager->input_manager, name);
if (!seat->seat) {
wl_list_init(&seat->seat_destroy.link);
ext_transient_seat_v1_send_denied(seat->resource);
return;
}
seat->seat_destroy.notify = transient_seat_handle_seat_destroy;
wl_signal_add(&seat->seat->events.destroy, &seat->seat_destroy);
uint32_t global_name = wl_global_get_name(seat->seat->wlr_seat->global, client);
ext_transient_seat_v1_send_ready(seat->resource, global_name);
}
static void manager_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct ext_transient_seat_manager_v1_interface transient_seat_manager_impl = {
.create = manager_handle_create,
.destroy = manager_handle_destroy,
};
static void transient_seat_manager_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &ext_transient_seat_manager_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
struct transient_seat_manager *manager = data;
wl_resource_set_implementation(resource, &transient_seat_manager_impl, manager, NULL);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct transient_seat_manager *manager = wl_container_of(listener, manager, server_destroy);
wl_list_remove(&manager->server_destroy.link);
free(manager);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
struct transient_seat_manager *manager = wl_container_of(listener, manager, display_destroy);
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
}
bool transient_seat_manager_create(struct input_manager *input_manager)
{
struct transient_seat_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
struct server *server = input_manager->server;
manager->input_manager = input_manager;
manager->global = wl_global_create(server->display, &ext_transient_seat_manager_v1_interface, 1,
manager, transient_seat_manager_bind);
if (!manager->global) {
kywc_log(KYWC_WARN, "Failed to create ext_transient_seat_manager_v1");
free(manager);
return false;
}
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &manager->server_destroy);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &manager->display_destroy);
return true;
}
kylin-wayland-compositor/src/input/config.c 0000664 0001750 0001750 00000120316 15160460057 020025 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include "config.h"
#include "input_p.h"
#include "util/dbus.h"
static const char *service_input_path = "/com/kylin/Wlcom/Input";
static const char *service_input_interface = "com.kylin.Wlcom.Input";
static const char *service_seat_path = "/com/kylin/Wlcom/Seat";
static const char *service_seat_interface = "com.kylin.Wlcom.Seat";
static const char *input_type_map[] = {
[WLR_INPUT_DEVICE_KEYBOARD] = "keyboard", [WLR_INPUT_DEVICE_POINTER] = "pointer",
[WLR_INPUT_DEVICE_TOUCH] = "touch", [WLR_INPUT_DEVICE_TABLET] = "table-tool",
[WLR_INPUT_DEVICE_TABLET_PAD] = "table-pad", [WLR_INPUT_DEVICE_SWITCH] = "switch",
};
void input_prop_and_state_debug(struct input *input)
{
struct input_prop *prop = &input->prop;
struct input_state *default_state = &input->default_state;
struct input_state *state = &input->state;
kywc_log(KYWC_DEBUG, "Input %s(%s) prop debug", input->name, input_type_map[prop->type]);
kywc_log(KYWC_DEBUG, "\tsend_event_modes = %d", prop->send_events_modes);
kywc_log(KYWC_DEBUG, "\t\tsend_events_mode = %d (%d)", state->send_events_mode,
default_state->send_events_mode);
if (prop->click_methods != 0) {
kywc_log(KYWC_DEBUG, "\tclick_methods = %d", prop->click_methods);
kywc_log(KYWC_DEBUG, "\t\tclick_method = %d (%d)", state->click_method,
default_state->click_method);
}
if (prop->tap_finger_count > 0) {
kywc_log(KYWC_DEBUG, "\ttap_finger_count = %d", prop->tap_finger_count);
kywc_log(KYWC_DEBUG, "\t\ttap_to_click = %d (%d)", state->tap_to_click,
default_state->tap_to_click);
kywc_log(KYWC_DEBUG, "\t\ttap_button_map = %d (%d)", state->tap_button_map,
default_state->tap_button_map);
kywc_log(KYWC_DEBUG, "\t\ttap_and_drag = %d (%d)", state->tap_and_drag,
default_state->tap_and_drag);
kywc_log(KYWC_DEBUG, "\t\ttap_drag_lock = %d (%d)", state->tap_drag_lock,
default_state->tap_drag_lock);
}
if (prop->scroll_methods != 0) {
kywc_log(KYWC_DEBUG, "\tscroll_methods = %d", prop->scroll_methods);
kywc_log(KYWC_DEBUG, "\t\tscroll_method = %d (%d)", state->scroll_method,
default_state->scroll_method);
if (prop->scroll_methods & 0x4) {
kywc_log(KYWC_DEBUG, "\t\tscroll_button = %d (%d)", state->scroll_button,
default_state->scroll_button);
kywc_log(KYWC_DEBUG, "\t\tscroll_button_lock = %d (%d)", state->scroll_button_lock,
default_state->scroll_button_lock);
}
}
if (prop->has_pointer_accel) {
kywc_log(KYWC_DEBUG, "\thas_pointer_accel = %d", prop->has_pointer_accel);
kywc_log(KYWC_DEBUG, "\t\tpointer_accel_speed = %f (%f)", state->pointer_accel_speed,
default_state->pointer_accel_speed);
kywc_log(KYWC_DEBUG, "\taccel_profiles = %d", prop->accel_profiles);
kywc_log(KYWC_DEBUG, "\t\taccel_profile = %d (%d)", state->accel_profile,
default_state->accel_profile);
}
if (prop->has_calibration_matrix) {
kywc_log(KYWC_DEBUG, "\thas_calibration_matrix = %d", prop->has_calibration_matrix);
kywc_log(KYWC_DEBUG,
"\t\tcalibration_set_matrix(%f, %f, %f, %f, %f, %f) (%f, %f, %f, %f, %f, %f)",
state->calibration_matrix[0], state->calibration_matrix[1],
state->calibration_matrix[2], state->calibration_matrix[3],
state->calibration_matrix[4], state->calibration_matrix[5],
default_state->calibration_matrix[0], default_state->calibration_matrix[1],
default_state->calibration_matrix[2], default_state->calibration_matrix[3],
default_state->calibration_matrix[4], default_state->calibration_matrix[5]);
}
if (prop->has_natural_scroll) {
kywc_log(KYWC_DEBUG, "\thas_natural_scroll = %d", prop->has_natural_scroll);
kywc_log(KYWC_DEBUG, "\t\tnatural_scroll = %d (%d)", state->natural_scroll,
default_state->natural_scroll);
}
if (prop->has_left_handed) {
kywc_log(KYWC_DEBUG, "\thas_left_handed = %d", prop->has_left_handed);
kywc_log(KYWC_DEBUG, "\t\tleft_handed = %d (%d)", state->left_handed,
default_state->left_handed);
}
if (prop->has_middle_emulation) {
kywc_log(KYWC_DEBUG, "\thas_middle_emulation = %d", prop->has_middle_emulation);
kywc_log(KYWC_DEBUG, "\t\tmiddle_emulation = %d (%d)", state->middle_emulation,
state->middle_emulation);
}
if (prop->has_dwt) {
kywc_log(KYWC_DEBUG, "\thas_dwt = %d", prop->has_dwt);
kywc_log(KYWC_DEBUG, "\t\tdwt = %d (%d)", state->dwt, default_state->dwt);
}
if (prop->has_dwtp) {
kywc_log(KYWC_DEBUG, "\thas_dwtp = %d", prop->has_dwtp);
kywc_log(KYWC_DEBUG, "\t\tdwtp = %d (%d)", state->dwtp, default_state->dwtp);
}
if (prop->has_rotation) {
kywc_log(KYWC_DEBUG, "\thas_rotation = %d", prop->has_rotation);
kywc_log(KYWC_DEBUG, "\t\trotation_angle = %d (%d)", state->rotation_angle,
default_state->rotation_angle);
}
}
static int list_inputs(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
struct input_manager *manager = userdata;
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(m, &reply));
CK(sd_bus_message_open_container(reply, 'a', "(su)"));
struct input *input;
wl_list_for_each(input, &manager->inputs, link) {
if (input->prop.is_virtual) {
continue;
}
sd_bus_message_append(reply, "(su)", input->name, input->prop.prop);
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
static int map_to_output(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL, *output_name = NULL;
CK(sd_bus_message_read(m, "ss", &input_name, &output_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
bool none_output = !strcmp(output_name, "none");
if (!none_output) {
struct kywc_output *kywc_output = kywc_output_by_name(output_name);
if (!kywc_output || !kywc_output->state.enabled) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid output or disabled.");
return sd_bus_reply_method_error(m, &error);
}
}
const char *current = input->mapped_output ? input->state.mapped_to_output : NULL;
if (input->prop.support_mapped_to_output && (!current || strcmp(current, output_name))) {
struct input_state state = input->state;
state.mapped_to_output = none_output ? NULL : output_name;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int change_seat(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL, *seat_name = NULL;
CK(sd_bus_message_read(m, "ss", &input_name, &seat_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (strncmp(seat_name, "seat", 4)) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid seat.");
return sd_bus_reply_method_error(m, &error);
}
if (strcmp(input->state.seat, seat_name)) {
struct input_state state = input->state;
state.seat = seat_name;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_send_events(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "uu", input->state.send_events_mode,
input->default_state.send_events_mode);
}
static int set_send_events(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
uint32_t mode = 0;
CK(sd_bus_message_read(m, "su", &input_name, &mode));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->prop.send_events_modes < mode) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid mode.");
return sd_bus_reply_method_error(m, &error);
}
if (input->state.send_events_mode != mode) {
struct input_state state = input->state;
state.send_events_mode = mode;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_tap_to_click(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "bb", input->state.tap_to_click,
input->default_state.tap_to_click);
}
static int enable_tap_to_click(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
int32_t enabled = 0;
CK(sd_bus_message_read(m, "sb", &input_name, &enabled));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->prop.tap_finger_count && input->state.tap_to_click != enabled) {
struct input_state state = input->state;
state.tap_to_click = enabled;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_tap_and_drag(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "bb", input->state.tap_and_drag,
input->default_state.tap_and_drag);
}
static int enable_tap_and_drag(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
int32_t enabled = 0;
CK(sd_bus_message_read(m, "sb", &input_name, &enabled));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->prop.tap_finger_count && input->state.tap_and_drag != enabled) {
struct input_state state = input->state;
state.tap_and_drag = enabled;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_pointer_speed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "dd", input->state.pointer_accel_speed,
input->default_state.pointer_accel_speed);
}
static int set_pointer_speed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
double speed = 0.0f;
CK(sd_bus_message_read(m, "sd", &input_name, &speed));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (speed < -1.0f || speed > 1.0f) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid speed.");
return sd_bus_reply_method_error(m, &error);
}
if (input->prop.has_pointer_accel && input->state.pointer_accel_speed != speed) {
struct input_state state = input->state;
state.pointer_accel_speed = speed;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_accel_profile(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "uu", input->state.accel_profile,
input->default_state.accel_profile);
}
static int set_accel_profile(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
uint32_t accel_profile;
CK(sd_bus_message_read(m, "su", &input_name, &accel_profile));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->prop.has_pointer_accel && input->prop.accel_profiles &&
input->state.accel_profile != accel_profile) {
struct input_state state = input->state;
state.accel_profile = accel_profile;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_scroll_method(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "uu", input->state.scroll_method,
input->default_state.scroll_method);
}
static int set_scroll_method(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
uint32_t scroll_method;
CK(sd_bus_message_read(m, "su", &input_name, &scroll_method));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->prop.scroll_methods && input->state.scroll_method != scroll_method) {
struct input_state state = input->state;
state.scroll_method = scroll_method;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_disable_while_typing(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "bb", input->state.dwt, input->default_state.dwt);
}
static int set_disable_while_typing(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
uint32_t dwt;
CK(sd_bus_message_read(m, "sb", &input_name, &dwt));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->prop.has_dwt && input->state.dwt != dwt) {
struct input_state state = input->state;
state.dwt = dwt;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_natural_scroll(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "bb", input->state.natural_scroll,
input->default_state.natural_scroll);
}
static int enable_natural_scroll(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
int32_t enabled = 0;
CK(sd_bus_message_read(m, "sb", &input_name, &enabled));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->prop.has_natural_scroll && input->state.natural_scroll != enabled) {
struct input_state state = input->state;
state.natural_scroll = enabled;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_left_handed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "bb", input->state.left_handed,
input->default_state.left_handed);
}
static int enable_left_handed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
int32_t enabled = 0;
CK(sd_bus_message_read(m, "sb", &input_name, &enabled));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->prop.has_left_handed && input->state.left_handed != enabled) {
struct input_state state = input->state;
state.left_handed = enabled;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_repeat_info(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "iiii", input->state.repeat_rate,
input->state.repeat_delay, input->default_state.repeat_rate,
input->default_state.repeat_delay);
}
static int set_repeat_info(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
int32_t rate, delay;
CK(sd_bus_message_read(m, "sii", &input_name, &rate, &delay));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->prop.type == WLR_INPUT_DEVICE_KEYBOARD &&
(input->state.repeat_rate != rate || input->state.repeat_delay != delay)) {
struct input_state state = input->state;
state.repeat_rate = rate;
state.repeat_delay = delay;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_scroll_factor(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "dd", input->state.scroll_factor,
input->default_state.scroll_factor);
}
static int set_scroll_factor(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
double scroll_factor;
CK(sd_bus_message_read(m, "sd", &input_name, &scroll_factor));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->state.scroll_factor != scroll_factor) {
struct input_state state = input->state;
state.scroll_factor = scroll_factor;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static int get_double_click_time(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
CK(sd_bus_message_read(m, "s", &input_name));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
return sd_bus_reply_method_return(m, "uu", input->state.double_click_time,
input->default_state.double_click_time);
}
static int set_double_click_time(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *input_name = NULL;
uint32_t double_click_time;
CK(sd_bus_message_read(m, "su", &input_name, &double_click_time));
struct input *input = input_by_name(input_name);
if (!input) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid input.");
return sd_bus_reply_method_error(m, &error);
}
if (input->state.double_click_time != double_click_time) {
struct input_state state = input->state;
state.double_click_time = double_click_time;
input_set_state(input, &state);
}
return sd_bus_reply_method_return(m, NULL);
}
static const sd_bus_vtable service_input_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("ListAllInputs", "", "a(su)", list_inputs, 0),
SD_BUS_METHOD("MapToOutput", "ss", "", map_to_output, 0),
SD_BUS_METHOD("ChangeSeat", "ss", "", change_seat, 0),
SD_BUS_METHOD("GetSendEventsMode", "s", "uu", get_send_events, 0),
SD_BUS_METHOD("SetSendEventsMode", "su", "", set_send_events, 0),
SD_BUS_METHOD("GetTapToClick", "s", "bb", get_tap_to_click, 0),
SD_BUS_METHOD("EnableTapToClick", "sb", "", enable_tap_to_click, 0),
SD_BUS_METHOD("GetTapAndDrag", "s", "bb", get_tap_and_drag, 0),
SD_BUS_METHOD("EnableTapAndDrag", "sb", "", enable_tap_and_drag, 0),
SD_BUS_METHOD("GetPointerSpeed", "s", "dd", get_pointer_speed, 0),
SD_BUS_METHOD("SetPointerSpeed", "sd", "", set_pointer_speed, 0),
SD_BUS_METHOD("GetAccelProfile", "s", "uu", get_accel_profile, 0),
SD_BUS_METHOD("SetAccelProfile", "su", "", set_accel_profile, 0),
SD_BUS_METHOD("GetScrollMethod", "s", "uu", get_scroll_method, 0),
SD_BUS_METHOD("SetScrollMethod", "su", "", set_scroll_method, 0),
SD_BUS_METHOD("GetDisableWhileTyping", "s", "bb", get_disable_while_typing, 0),
SD_BUS_METHOD("SetDisableWhileTyping", "sb", "", set_disable_while_typing, 0),
SD_BUS_METHOD("GetNaturalScroll", "s", "bb", get_natural_scroll, 0),
SD_BUS_METHOD("EnableNaturalScroll", "sb", "", enable_natural_scroll, 0),
SD_BUS_METHOD("GetLeftHand", "s", "bb", get_left_handed, 0),
SD_BUS_METHOD("EnableLeftHand", "sb", "", enable_left_handed, 0),
SD_BUS_METHOD("GetRepeatInfo", "s", "iiii", get_repeat_info, 0),
SD_BUS_METHOD("SetRepeatInfo", "sii", "", set_repeat_info, 0),
SD_BUS_METHOD("GetScrollFactor", "s", "dd", get_scroll_factor, 0),
SD_BUS_METHOD("SetScrollFactor", "sd", "", set_scroll_factor, 0),
SD_BUS_METHOD("GetDoubleClickTime", "s", "uu", get_double_click_time, 0),
SD_BUS_METHOD("SetDoubleClickTime", "su", "", set_double_click_time, 0),
SD_BUS_VTABLE_END,
};
static int list_seats(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
struct input_manager *manager = userdata;
sd_bus_message *reply = NULL;
CK(sd_bus_message_new_method_return(m, &reply));
CK(sd_bus_message_open_container(reply, 'a', "(ss)"));
struct seat *seat;
wl_list_for_each(seat, &manager->seats, link) {
json_object *config = json_object_object_get(manager->seat_config->json, seat->name);
const char *cfg = json_object_to_json_string(config);
sd_bus_message_append(reply, "(ss)", seat->name, cfg);
}
CK(sd_bus_message_close_container(reply));
CK(sd_bus_send(NULL, reply, NULL));
sd_bus_message_unref(reply);
return 1;
}
static int set_cursor(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *seat_name, *cursor_theme;
uint32_t cursor_size;
CK(sd_bus_message_read(m, "ssu", &seat_name, &cursor_theme, &cursor_size));
struct seat *seat = seat_by_name(seat_name);
if (!seat) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid seat.");
return sd_bus_reply_method_error(m, &error);
}
seat_set_cursor(seat, cursor_theme, cursor_size);
return sd_bus_reply_method_return(m, NULL);
}
static int set_lock_keys_mode(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *seat_name;
uint32_t mode;
CK(sd_bus_message_read(m, "su", &seat_name, &mode));
struct seat *seat = seat_by_name(seat_name);
if (!seat) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid seat.");
return sd_bus_reply_method_error(m, &error);
}
seat->state.keyboard_lock_mode = mode;
seat_write_config(seat);
return sd_bus_reply_method_return(m, NULL);
}
static int get_focused_client(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
{
const char *seat_name;
CK(sd_bus_message_read(m, "s", &seat_name));
struct seat *seat = seat_by_name(seat_name);
if (!seat) {
const sd_bus_error error =
SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid seat.");
return sd_bus_reply_method_error(m, &error);
}
struct wlr_seat_client *client = seat->wlr_seat->keyboard_state.focused_client;
pid_t pid = 0; // no focused client
if (client) {
wl_client_get_credentials(client->client, &pid, NULL, NULL);
}
return sd_bus_reply_method_return(m, "u", pid);
}
static const sd_bus_vtable service_seat_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("ListAllSeats", "", "a(ss)", list_seats, 0),
SD_BUS_METHOD("SetCursor", "ssu", "", set_cursor, 0),
SD_BUS_METHOD("SetLockKeysMode", "su", "", set_lock_keys_mode, 0),
SD_BUS_METHOD("GetFocusedClient", "s", "u", get_focused_client, 0),
SD_BUS_VTABLE_END,
};
void input_notify_destroy(struct input *input)
{
if (!input->device) {
return;
}
struct input_manager *manager = input->manager;
if (!manager->config) {
return;
}
dbus_emit_signal(service_input_path, service_input_interface, "input_destroy", "s",
input->name);
}
void input_notify_create(struct input *input)
{
if (!input->device) {
return;
}
struct input_manager *manager = input->manager;
if (!manager->config) {
return;
}
dbus_emit_signal(service_input_path, service_input_interface, "input_create", "su", input->name,
input->prop.prop);
}
bool input_manager_config_init(struct input_manager *input_manager)
{
input_manager->config = config_manager_add_config("Inputs");
if (!input_manager->config) {
return false;
}
dbus_register_object(NULL, service_input_path, service_input_interface, service_input_vtable,
input_manager);
input_manager->seat_config = config_manager_add_config("Seats");
if (!input_manager->seat_config) {
return false;
}
dbus_register_object(NULL, service_seat_path, service_seat_interface, service_seat_vtable,
input_manager);
return true;
}
bool input_read_config(struct input *input, struct input_state *state)
{
struct input_manager *manager = input->manager;
if (!manager->config || !manager->config->json) {
return false;
}
json_object *config = json_object_object_get(manager->config->json, input->name);
if (!config) {
return false;
}
json_object *data;
if (json_object_object_get_ex(config, "mapped_to_output", &data)) {
state->mapped_to_output = json_object_get_string(data);
}
if (json_object_object_get_ex(config, "seat", &data)) {
state->seat = json_object_get_string(data);
}
if (json_object_object_get_ex(config, "send_events_mode", &data)) {
state->send_events_mode = json_object_get_int(data);
}
if (input->prop.tap_finger_count > 0) {
if (json_object_object_get_ex(config, "tap_to_click", &data)) {
state->tap_to_click = json_object_get_boolean(data);
}
if (json_object_object_get_ex(config, "tap_button_map", &data)) {
state->tap_button_map = json_object_get_int(data);
}
if (json_object_object_get_ex(config, "tap_and_drag", &data)) {
state->tap_and_drag = json_object_get_boolean(data);
}
if (json_object_object_get_ex(config, "tap_drag_lock", &data)) {
state->tap_drag_lock = json_object_get_boolean(data);
}
}
if (input->prop.has_natural_scroll) {
if (json_object_object_get_ex(config, "natural_scroll", &data)) {
state->natural_scroll = json_object_get_boolean(data);
}
}
if (input->prop.has_middle_emulation) {
if (json_object_object_get_ex(config, "middle_emulation", &data)) {
state->middle_emulation = json_object_get_boolean(data);
}
}
if (input->prop.has_left_handed) {
if (json_object_object_get_ex(config, "left_handed", &data)) {
state->left_handed = json_object_get_boolean(data);
}
}
if (input->prop.has_dwt) {
if (json_object_object_get_ex(config, "dwt", &data)) {
state->dwt = json_object_get_boolean(data);
}
}
if (input->prop.has_dwtp) {
if (json_object_object_get_ex(config, "dwtp", &data)) {
state->dwtp = json_object_get_boolean(data);
}
}
if (input->prop.scroll_methods) {
if (json_object_object_get_ex(config, "scroll_method", &data)) {
state->scroll_method = json_object_get_int(data);
}
if (input->prop.scroll_methods & 0x4) {
if (json_object_object_get_ex(config, "scroll_button", &data)) {
state->scroll_button = json_object_get_int(data);
}
if (json_object_object_get_ex(config, "scroll_button_lock", &data)) {
state->scroll_button_lock = json_object_get_boolean(data);
}
}
}
if (input->prop.click_methods) {
if (json_object_object_get_ex(config, "click_method", &data)) {
state->click_method = json_object_get_int(data);
}
}
if (input->prop.has_pointer_accel) {
if (json_object_object_get_ex(config, "pointer_accel_speed", &data)) {
state->pointer_accel_speed = json_object_get_double(data);
}
if (input->prop.accel_profiles) {
if (json_object_object_get_ex(config, "accel_profile", &data)) {
state->accel_profile = json_object_get_int(data);
}
}
}
if (input->prop.has_calibration_matrix) {
if (json_object_object_get_ex(config, "calibration_matrix", &data)) {
for (int i = 0; i < 6; i++) {
state->calibration_matrix[i] =
json_object_get_double(json_object_array_get_idx(data, i));
}
}
}
if (json_object_object_get_ex(config, "scroll_factor", &data)) {
state->scroll_factor = json_object_get_double(data);
}
if (json_object_object_get_ex(config, "double_click_time", &data)) {
state->double_click_time = json_object_get_int(data);
}
if (input->prop.type == WLR_INPUT_DEVICE_KEYBOARD) {
if (json_object_object_get_ex(config, "repeat_delay", &data)) {
state->repeat_delay = json_object_get_int(data);
}
if (json_object_object_get_ex(config, "repeat_rate", &data)) {
state->repeat_rate = json_object_get_int(data);
}
}
return true;
}
#define WRITE_CONFIG(entry, type) \
if (state->entry == default_state->entry) { \
json_object_object_del(config, #entry); \
} else { \
json_object_object_add(config, #entry, json_object_new_##type(state->entry)); \
}
void input_write_config(struct input *input)
{
struct input_manager *manager = input->manager;
if (!manager->config || !manager->config->json) {
return;
}
struct input_state *state = &input->state;
json_object *config = json_object_object_get(manager->config->json, input->name);
if (!config) {
config = json_object_new_object();
json_object_object_add(manager->config->json, input->name, config);
}
if (state->mapped_to_output) {
json_object_object_add(config, "mapped_to_output",
json_object_new_string(state->mapped_to_output));
} else {
json_object_object_del(config, "mapped_to_output");
}
if (state->seat && strcmp(state->seat, "seat0")) {
json_object_object_add(config, "seat", json_object_new_string(state->seat));
} else {
json_object_object_del(config, "seat");
}
/* filter by default state */
struct input_state *default_state = &input->default_state;
WRITE_CONFIG(send_events_mode, int);
if (input->prop.tap_finger_count > 0) {
WRITE_CONFIG(tap_to_click, boolean);
WRITE_CONFIG(tap_button_map, int);
WRITE_CONFIG(tap_and_drag, boolean);
WRITE_CONFIG(tap_drag_lock, boolean);
}
if (input->prop.has_natural_scroll) {
WRITE_CONFIG(natural_scroll, boolean);
}
if (input->prop.has_middle_emulation) {
WRITE_CONFIG(middle_emulation, boolean);
}
if (input->prop.has_left_handed) {
WRITE_CONFIG(left_handed, boolean);
}
if (input->prop.has_dwt) {
WRITE_CONFIG(dwt, boolean);
}
if (input->prop.has_dwtp) {
WRITE_CONFIG(dwtp, boolean);
}
if (input->prop.scroll_methods) {
WRITE_CONFIG(scroll_method, int);
if (input->prop.scroll_methods & 0x4) {
WRITE_CONFIG(scroll_button, int);
WRITE_CONFIG(scroll_button_lock, boolean);
}
}
if (input->prop.click_methods) {
WRITE_CONFIG(click_method, int);
}
if (input->prop.has_pointer_accel) {
WRITE_CONFIG(pointer_accel_speed, double);
if (input->prop.accel_profiles) {
WRITE_CONFIG(accel_profile, int);
}
}
if (input->prop.has_calibration_matrix) {
if (memcmp(state->calibration_matrix, default_state->calibration_matrix,
sizeof(state->calibration_matrix)) == 0) {
json_object_object_del(config, "calibration_matrix");
} else {
json_object *matrix = json_object_new_array_ext(6);
for (int i = 0; i < 6; i++) {
json_object_array_add(matrix, json_object_new_double(state->calibration_matrix[i]));
}
json_object_object_add(config, "calibration_matrix", matrix);
}
}
// TODO: rotation angle
WRITE_CONFIG(scroll_factor, double);
WRITE_CONFIG(double_click_time, int);
if (input->prop.type == WLR_INPUT_DEVICE_KEYBOARD) {
WRITE_CONFIG(repeat_delay, int);
WRITE_CONFIG(repeat_rate, int);
}
}
#undef WRITE_CONFIG
bool seat_read_config(struct seat *seat)
{
if (!seat->manager->seat_config || !seat->manager->seat_config->json) {
return false;
}
json_object *config = json_object_object_get(seat->manager->seat_config->json, seat->name);
if (!config) {
return true;
}
json_object *data;
if (json_object_object_get_ex(config, "cursor_theme", &data)) {
seat->state.cursor_theme = strdup(json_object_get_string(data));
}
if (json_object_object_get_ex(config, "cursor_size", &data)) {
seat->state.cursor_size = json_object_get_int(data);
}
if (json_object_object_get_ex(config, "keyboard_lock_mode", &data)) {
seat->state.keyboard_lock_mode = json_object_get_int(data);
}
if (seat->state.keyboard_lock_mode == 0) {
seat->state.keyboard_lock = 0;
} else if (seat->state.keyboard_lock_mode == 1) {
seat->state.keyboard_lock = 7;
} else {
if (json_object_object_get_ex(config, "keyboard_lock", &data)) {
seat->state.keyboard_lock = json_object_get_int(data);
}
}
return true;
}
void seat_write_config(struct seat *seat)
{
if (!seat->manager->seat_config) {
return;
}
json_object *config = json_object_object_get(seat->manager->seat_config->json, seat->name);
if (!config) {
config = json_object_new_object();
json_object_object_add(seat->manager->seat_config->json, seat->name, config);
}
if (seat->state.cursor_theme && strcmp(seat->state.cursor_theme, "default")) {
json_object_object_add(config, "cursor_theme",
json_object_new_string(seat->state.cursor_theme));
} else {
json_object_object_del(config, "cursor_theme");
}
if (seat->state.cursor_size != 24) {
json_object_object_add(config, "cursor_size", json_object_new_int(seat->state.cursor_size));
} else {
json_object_object_del(config, "cursor_size");
}
json_object_object_add(config, "keyboard_lock_mode",
json_object_new_int(seat->state.keyboard_lock_mode));
json_object_object_add(config, "keyboard_lock", json_object_new_int(seat->state.keyboard_lock));
}
kylin-wayland-compositor/src/input/text_input_v2.c 0000664 0001750 0001750 00000027024 15160461067 021376 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include "text-input-unstable-v2-protocol.h"
#include "text_input_v2.h"
static void text_input_destroy(struct text_input_v2 *text_input)
{
wl_signal_emit_mutable(&text_input->events.destroy, NULL);
assert(wl_list_empty(&text_input->events.enable.listener_list));
assert(wl_list_empty(&text_input->events.disable.listener_list));
assert(wl_list_empty(&text_input->events.commit.listener_list));
assert(wl_list_empty(&text_input->events.destroy.listener_list));
wl_resource_set_user_data(text_input->resource, NULL);
wl_list_remove(&text_input->surface_destroy.link);
wl_list_remove(&text_input->seat_destroy.link);
wl_list_remove(&text_input->link);
free(text_input->surrounding.text);
free(text_input);
}
static void text_input_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void text_input_enable(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *surface)
{
struct text_input_v2 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
text_input->surface = wlr_surface_from_resource(surface);
wl_signal_add(&text_input->surface->events.destroy, &text_input->surface_destroy);
text_input->enabled = true;
wl_signal_emit_mutable(&text_input->events.enable, NULL);
}
static void text_input_disable(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *surface)
{
struct text_input_v2 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
text_input->enabled = false;
wl_signal_emit_mutable(&text_input->events.disable, NULL);
text_input->surface = NULL;
wl_list_remove(&text_input->surface_destroy.link);
wl_list_init(&text_input->surface_destroy.link);
}
static void text_input_show_input_panel(struct wl_client *client, struct wl_resource *resource)
{
// Not implemented yet
}
static void text_input_hide_input_panel(struct wl_client *client, struct wl_resource *resource)
{
// Not implemented yet
}
static void text_input_set_surrounding_text(struct wl_client *client, struct wl_resource *resource,
const char *text, int32_t cursor, int32_t anchor)
{
struct text_input_v2 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
free(text_input->surrounding.text);
text_input->surrounding.text = strdup(text);
if (!text_input->surrounding.text) {
wl_client_post_no_memory(client);
}
text_input->surrounding.cursor = cursor;
text_input->surrounding.anchor = anchor;
text_input->surrounding.pending = true;
}
static void text_input_set_content_type(struct wl_client *client, struct wl_resource *resource,
uint32_t hint, uint32_t purpose)
{
struct text_input_v2 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
/* convert text_input_v2 to text_input_v3 */
text_input->content_type.hint = hint;
text_input->content_type.purpose =
purpose > ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PASSWORD ? purpose + 1 : purpose;
text_input->content_type.pending = true;
}
static void text_input_set_cursor_rectangle(struct wl_client *client, struct wl_resource *resource,
int32_t x, int32_t y, int32_t width, int32_t height)
{
struct text_input_v2 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
text_input->cursor_rectangle.x = x;
text_input->cursor_rectangle.y = y;
text_input->cursor_rectangle.width = width;
text_input->cursor_rectangle.height = height;
}
static void text_input_set_preferred_language(struct wl_client *client,
struct wl_resource *resource, const char *language)
{
// Not implemented yet
}
static void text_input_update_state(struct wl_client *client, struct wl_resource *resource,
uint32_t serial, uint32_t reason)
{
struct text_input_v2 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
if (text_input->surface == NULL) {
kywc_log(KYWC_DEBUG, "Text input commit when surface destroyed");
}
text_input->serial = serial;
wl_signal_emit_mutable(&text_input->events.commit, NULL);
text_input->surrounding.pending = false;
text_input->content_type.pending = false;
}
static const struct zwp_text_input_v2_interface text_input_impl = {
.destroy = text_input_handle_destroy,
.enable = text_input_enable,
.disable = text_input_disable,
.show_input_panel = text_input_show_input_panel,
.hide_input_panel = text_input_hide_input_panel,
.set_surrounding_text = text_input_set_surrounding_text,
.set_content_type = text_input_set_content_type,
.set_cursor_rectangle = text_input_set_cursor_rectangle,
.set_preferred_language = text_input_set_preferred_language,
.update_state = text_input_update_state,
};
static void text_input_resource_destroy(struct wl_resource *resource)
{
struct text_input_v2 *text_input = wl_resource_get_user_data(resource);
if (!text_input) {
return;
}
text_input_destroy(text_input);
}
static void text_input_handle_surface_destroy(struct wl_listener *listener, void *data)
{
struct text_input_v2 *text_input = wl_container_of(listener, text_input, surface_destroy);
wl_list_remove(&text_input->surface_destroy.link);
wl_list_init(&text_input->surface_destroy.link);
text_input->surface = NULL;
}
static void text_input_handle_seat_destroy(struct wl_listener *listener, void *data)
{
struct text_input_v2 *text_input = wl_container_of(listener, text_input, seat_destroy);
text_input_destroy(text_input);
}
static void text_input_manager_get_text_input(struct wl_client *client,
struct wl_resource *resource, uint32_t id,
struct wl_resource *seat)
{
int version = wl_resource_get_version(resource);
struct wl_resource *text_input_resource =
wl_resource_create(client, &zwp_text_input_v2_interface, version, id);
if (text_input_resource == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(text_input_resource, &text_input_impl, NULL,
text_input_resource_destroy);
struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat);
if (seat_client == NULL) {
return;
}
struct text_input_v2 *text_input = calloc(1, sizeof(*text_input));
if (!text_input) {
wl_client_post_no_memory(client);
return;
}
wl_signal_init(&text_input->events.enable);
wl_signal_init(&text_input->events.commit);
wl_signal_init(&text_input->events.disable);
wl_signal_init(&text_input->events.destroy);
text_input->resource = text_input_resource;
wl_resource_set_user_data(text_input_resource, text_input);
text_input->seat = seat_client->seat;
wl_signal_add(&seat_client->events.destroy, &text_input->seat_destroy);
text_input->seat_destroy.notify = text_input_handle_seat_destroy;
text_input->surface_destroy.notify = text_input_handle_surface_destroy;
wl_list_init(&text_input->surface_destroy.link);
struct text_input_manager_v2 *manager = wl_resource_get_user_data(resource);
wl_list_insert(&manager->text_inputs, &text_input->link);
wl_signal_emit_mutable(&manager->events.text_input, text_input);
}
static void text_input_manager_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct zwp_text_input_manager_v2_interface text_input_manager_impl = {
.destroy = text_input_manager_destroy,
.get_text_input = text_input_manager_get_text_input,
};
static void text_input_manager_bind(struct wl_client *wl_client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(wl_client, &zwp_text_input_manager_v2_interface, version, id);
if (resource == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
struct text_input_manager_v2 *manager = data;
wl_resource_set_implementation(resource, &text_input_manager_impl, manager, NULL);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
struct text_input_manager_v2 *manager = wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, manager);
assert(wl_list_empty(&manager->events.text_input.listener_list));
assert(wl_list_empty(&manager->events.destroy.listener_list));
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
struct text_input_manager_v2 *text_input_manager_v2_create(struct wl_display *display)
{
struct text_input_manager_v2 *manager = calloc(1, sizeof(*manager));
if (!manager) {
return NULL;
}
wl_list_init(&manager->text_inputs);
wl_signal_init(&manager->events.text_input);
wl_signal_init(&manager->events.destroy);
manager->global = wl_global_create(display, &zwp_text_input_manager_v2_interface, 1, manager,
text_input_manager_bind);
if (!manager->global) {
free(manager);
return NULL;
}
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->display_destroy);
return manager;
}
void text_input_v2_send_enter(struct text_input_v2 *text_input, struct wlr_surface *surface)
{
zwp_text_input_v2_send_enter(text_input->resource, text_input->serial, surface->resource);
}
void text_input_v2_send_leave(struct text_input_v2 *text_input)
{
assert(text_input->surface != NULL);
zwp_text_input_v2_send_leave(text_input->resource, text_input->serial,
text_input->surface->resource);
text_input->surface = NULL;
wl_list_remove(&text_input->surface_destroy.link);
wl_list_init(&text_input->surface_destroy.link);
}
void text_input_v2_send_preedit_string(struct text_input_v2 *text_input, const char *text,
int32_t cursor_begin)
{
zwp_text_input_v2_send_preedit_cursor(text_input->resource, cursor_begin);
zwp_text_input_v2_send_preedit_styling(text_input->resource, 0, text ? strlen(text) : 0,
ZWP_TEXT_INPUT_V2_PREEDIT_STYLE_DEFAULT);
zwp_text_input_v2_send_preedit_string(text_input->resource, text ? text : "", "");
}
void text_input_v2_send_commit_string(struct text_input_v2 *text_input, const char *text)
{
zwp_text_input_v2_send_commit_string(text_input->resource, text);
}
void text_input_v2_send_delete_surrounding_text(struct text_input_v2 *text_input, const char *text,
uint32_t before_length, uint32_t after_length)
{
zwp_text_input_v2_send_delete_surrounding_text(
text_input->resource, strlen(text) - before_length, after_length + before_length);
text_input_v2_send_commit_string(text_input, text);
}
kylin-wayland-compositor/src/input/text_input_v2.h 0000664 0001750 0001750 00000003503 15160460057 021375 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#ifndef _TEXT_INPUT_V2_H_
#define _TEXT_INPUT_V2_H_
#include
#include
struct text_input_v2 {
struct wl_resource *resource;
struct wl_list link;
struct wlr_seat *seat;
struct wl_listener seat_destroy;
struct wlr_surface *surface;
struct wl_listener surface_destroy;
struct wlr_box cursor_rectangle;
struct {
bool pending;
char *text;
uint32_t cursor;
uint32_t anchor;
} surrounding;
struct {
bool pending;
uint32_t hint;
uint32_t purpose;
} content_type;
uint32_t serial;
bool enabled;
struct {
struct wl_signal enable;
struct wl_signal disable;
struct wl_signal commit;
struct wl_signal destroy;
} events;
};
struct text_input_manager_v2 {
struct wl_global *global;
struct wl_list text_inputs;
struct wl_listener display_destroy;
struct {
struct wl_signal text_input;
struct wl_signal destroy;
} events;
};
struct text_input_manager_v2 *text_input_manager_v2_create(struct wl_display *display);
void text_input_v2_send_enter(struct text_input_v2 *text_input, struct wlr_surface *surface);
void text_input_v2_send_leave(struct text_input_v2 *text_input);
void text_input_v2_send_preedit_string(struct text_input_v2 *text_input, const char *text,
int32_t cursor_begin);
void text_input_v2_send_commit_string(struct text_input_v2 *text_input, const char *text);
void text_input_v2_send_delete_surrounding_text(struct text_input_v2 *text_input, const char *text,
uint32_t before_length, uint32_t after_length);
#endif /* _TEXT_INPUT_V2_H_ */
kylin-wayland-compositor/src/input/keyboard.c 0000664 0001750 0001750 00000052144 15160461067 020365 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include
#include
#include
#include "input/keyboard.h"
#include "input/keyboard_group.h"
#include "input/seat.h"
#include "input_p.h"
#include "util/macros.h"
#include "util/time.h"
static struct modifier {
char *name;
uint32_t mod;
} modifiers[] = {
{ XKB_MOD_NAME_SHIFT, WLR_MODIFIER_SHIFT },
{ XKB_MOD_NAME_CAPS, WLR_MODIFIER_CAPS },
{ "Ctrl", WLR_MODIFIER_CTRL },
{ XKB_MOD_NAME_CTRL, WLR_MODIFIER_CTRL },
{ "Alt", WLR_MODIFIER_ALT },
{ XKB_MOD_NAME_ALT, WLR_MODIFIER_ALT },
{ XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 },
{ "Mod3", WLR_MODIFIER_MOD3 },
{ "Win", WLR_MODIFIER_LOGO },
{ XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO },
{ "Mod5", WLR_MODIFIER_MOD5 },
};
uint32_t keyboard_get_modifier_mask_by_name(const char *name)
{
for (size_t i = 0; i < ARRAY_SIZE(modifiers); i++) {
if (strcasecmp(modifiers[i].name, name) == 0) {
return modifiers[i].mod;
}
}
return 0;
}
const char *keyboard_get_modifier_name_by_mask(uint32_t modifier)
{
for (size_t i = 0; i < ARRAY_SIZE(modifiers); i++) {
if (modifiers[i].mod == modifier) {
return modifiers[i].name;
}
}
return NULL;
}
const char *keyboard_get_modifier_names(uint32_t modifier_masks, char split)
{
static char names[128] = { 0 };
char *p = names;
for (size_t i = 0; i < ARRAY_SIZE(modifiers); i++) {
if ((modifier_masks & modifiers[i].mod) != 0) {
p += sprintf(p, "%s%c", modifiers[i].name, split);
modifier_masks ^= modifiers[i].mod;
}
}
p != names ? (*--p = '\0') : (*p = '\0');
return names;
}
static void modifiers_mask_debug(uint32_t mask, const char *mask_name)
{
const char *names = keyboard_get_modifier_names(mask, ' ');
kywc_log(KYWC_DEBUG, "\t%s: %s", mask_name, names);
}
// TODO: compose and dead key support
static void keyboard_state_erase_key(struct keyboard_state *keyboard_state, uint32_t keysym)
{
uint32_t idx = 0;
for (size_t i = 0; i < keyboard_state->npressed; i++) {
if (i > idx) {
keyboard_state->pressed_keysyms[idx] = keyboard_state->pressed_keysyms[i];
}
if (keyboard_state->pressed_keysyms[i] != keysym) {
idx++;
}
}
while (keyboard_state->npressed > idx) {
keyboard_state->npressed--;
keyboard_state->pressed_keysyms[keyboard_state->npressed] = 0;
}
if (kywc_log_get_level() == KYWC_DEBUG) {
kywc_log(KYWC_DEBUG, "Erase key 0x%x, %lu", keysym, keyboard_state->npressed);
for (size_t i = 0; i < keyboard_state->npressed; i++) {
kywc_log(KYWC_DEBUG, "\tcurrent keysym %lu: 0x%x", i,
keyboard_state->pressed_keysyms[i]);
}
}
}
static void keyboard_state_add_key(struct keyboard_state *keyboard_state, uint32_t keysym)
{
if (keyboard_state->npressed >= MAX_PRESSED_KEY) {
return;
}
if (keyboard_state->npressed > 0 &&
keyboard_state->pressed_keysyms[keyboard_state->npressed - 1] == keysym) {
return;
}
keyboard_state->pressed_keysyms[keyboard_state->npressed] = keysym;
keyboard_state->npressed++;
if (kywc_log_get_level() == KYWC_DEBUG) {
kywc_log(KYWC_DEBUG, "Add key 0x%x, %lu", keysym, keyboard_state->npressed);
for (size_t i = 0; i < keyboard_state->npressed; i++) {
kywc_log(KYWC_DEBUG, "\tcurrent keysym %lu: 0x%x", i,
keyboard_state->pressed_keysyms[i]);
}
}
}
static void keyboard_state_clear(struct keyboard_state *keyboard_state)
{
if (keyboard_state->npressed > 0) {
*keyboard_state = (struct keyboard_state){ 0 };
}
}
static void handle_keyboard_state(struct keyboard_state *keyboard_state, uint32_t modifiers,
uint32_t keysym, bool pressed)
{
bool last_key_is_modifiers = modifiers != keyboard_state->last_modifiers;
keyboard_state->only_one_modifier = modifiers && keyboard_state->last_modifiers == 0 &&
!pressed && keyboard_state->npressed == 1;
keyboard_state->last_modifiers = modifiers;
if (last_key_is_modifiers && keyboard_state->last_keysym) {
// a modifiier key preesed before this key, erase it
keyboard_state_erase_key(keyboard_state, keyboard_state->last_keysym);
keyboard_state->last_keysym = 0;
}
if (pressed) {
keyboard_state_add_key(keyboard_state, keysym);
keyboard_state->last_keysym = keysym;
} else {
keyboard_state_erase_key(keyboard_state, xkb_keysym_to_upper(keysym));
keyboard_state_erase_key(keyboard_state, xkb_keysym_to_lower(keysym));
}
}
static bool keyboard_update_keyboard_state(struct keyboard *keyboard, uint32_t key,
uint32_t modifiers, bool pressed)
{
struct keyboard_state *keyboard_state = &keyboard->state;
/* Translate libinput keycode -> xkbcommon keysym */
const xkb_keysym_t *keysyms;
size_t len = 0; // xkb_state may be null
if (keyboard->wlr_keyboard->xkb_state) {
len = xkb_state_key_get_syms(keyboard->wlr_keyboard->xkb_state, key + 8, &keysyms);
}
for (size_t i = 0; i < len; ++i) {
handle_keyboard_state(keyboard_state, modifiers, keysyms[i], pressed);
}
for (size_t i = 0; i < len; ++i) {
xkb_keysym_t keysym = keysyms[i];
if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) {
input_manager_switch_vt(keysym - XKB_KEY_XF86Switch_VT_1 + 1);
return false;
}
}
return true;
}
static struct key_binding *keyboard_get_key_binding(struct keyboard *keyboard, bool pressed)
{
struct keyboard_state *keyboard_state = &keyboard->state;
struct seat *seat = keyboard->seat;
if (seat_is_keyboard_shortcuts_inhibited(seat)) {
return NULL;
}
if (!pressed && !keyboard->state.only_one_modifier) {
return NULL;
}
return bindings_get_key_binding(keyboard_state);
}
static void keyboard_repeat_stop(struct keyboard *keyboard)
{
if (!keyboard->repeat.timer) {
return;
}
if (keyboard->repeat.key == 0) {
return;
}
keyboard->repeat.key = 0;
wl_event_source_timer_update(keyboard->repeat.timer, 0);
}
static void keyboard_repeat_start(struct keyboard *keyboard, uint32_t key, bool pressed)
{
if (!keyboard->repeat.timer) {
return;
}
if (keyboard->repeat.key > 0) {
if (keyboard->repeat.key == key && !pressed) {
keyboard_repeat_stop(keyboard);
}
return;
}
/* only enable key repeat when pressed state */
if (!pressed) {
return;
}
int32_t delay = keyboard->wlr_keyboard->repeat_info.delay;
if (delay > 0) {
keyboard->repeat.key = key;
if (wl_event_source_timer_update(keyboard->repeat.timer, delay) < 0) {
kywc_log(KYWC_DEBUG, "Failed to set key repeat timer");
}
} else if (keyboard->repeat.key > 0) {
keyboard_repeat_stop(keyboard);
}
}
static void keyboard_update_lock(struct keyboard *keyboard, uint32_t key, bool pressed)
{
if (key != KEY_SCROLLLOCK) {
return;
}
struct keyboard_group *group = keyboard_group_from_wlr_keyboard(keyboard->wlr_keyboard);
if (!group) {
return;
}
if (group->scroll_lock == 0 && pressed) {
group->scroll_lock = 2;
group->keyboard.leds |= WLR_LED_SCROLL_LOCK;
} else if (group->scroll_lock > 0 && !pressed) {
if (--group->scroll_lock == 0) {
// only used to bypass check in wlr_keyboard_led_update
group->keyboard.leds |= WLR_LED_SCROLL_LOCK;
}
}
}
static void keyboard_feed_key(struct keyboard *keyboard, uint32_t key, uint32_t state,
uint32_t time, uint32_t modifiers)
{
if (kywc_log_get_level() == KYWC_DEBUG) {
modifiers_mask_debug(modifiers, "modifiers");
}
struct seat *seat = keyboard->seat;
bool pressed = state == WL_KEYBOARD_KEY_STATE_PRESSED;
keyboard_update_lock(keyboard, key, pressed);
/* early return if key is sent by input method */
if (keyboard_is_from_input_method(keyboard)) {
wlr_seat_set_keyboard(seat->wlr_seat, keyboard->wlr_keyboard);
wlr_seat_keyboard_notify_key(seat->wlr_seat, time, key, state);
return;
}
struct seat_keyboard_key_event event = {
.device = input_from_wlr_input(&keyboard->wlr_keyboard->base),
.time_msec = time,
.keycode = key,
.pressed = pressed,
};
wl_signal_emit_mutable(&seat->events.keyboard_key, &event);
if (!keyboard_update_keyboard_state(keyboard, key, modifiers, pressed)) {
return;
}
struct key_binding *binding = keyboard_get_key_binding(keyboard, pressed);
bool bypass_grab = bindings_get_key_binding_bypass_grab(binding);
if (!bypass_grab && seat->keyboard_grab && seat->keyboard_grab->interface->key &&
seat->keyboard_grab->interface->key(seat->keyboard_grab, keyboard, time, key, pressed,
modifiers)) {
keyboard_state_clear(&keyboard->state);
if (key != keyboard->repeat.key) {
keyboard_repeat_stop(keyboard);
}
if (!bindings_get_key_binding_no_repeat(binding)) {
keyboard_repeat_start(keyboard, key, pressed);
}
return;
}
/* don't auto repeat for some bindings, like vt switch */
bool need_repeat = false;
bool handled = bindings_handle_key_binding(binding, &need_repeat);
if (handled) {
keyboard_update_keyboard_state(keyboard, key, modifiers, false);
need_repeat ? keyboard_repeat_start(keyboard, key, pressed)
: keyboard_repeat_stop(keyboard);
return;
}
keyboard_repeat_stop(keyboard);
handled = input_method_handle_key(keyboard, time, key, state);
if (handled) {
return;
}
wlr_seat_set_keyboard(seat->wlr_seat, keyboard->wlr_keyboard);
wlr_seat_keyboard_notify_key(seat->wlr_seat, time, key, state);
}
static void keyboard_feed_modifiers(struct keyboard *keyboard,
struct wlr_keyboard_modifiers *modifiers)
{
if (kywc_log_get_level() == KYWC_DEBUG) {
kywc_log(KYWC_DEBUG, "Keyboard modifiers update");
modifiers_mask_debug(modifiers->depressed, "depressed");
modifiers_mask_debug(modifiers->latched, "latched");
modifiers_mask_debug(modifiers->locked, "locked");
modifiers_mask_debug(modifiers->group, "group");
}
if (input_method_handle_modifiers(keyboard)) {
return;
}
wl_signal_emit_mutable(&keyboard->seat->events.keyboard_modifiers, modifiers);
struct wlr_seat *wlr_seat = keyboard->seat->wlr_seat;
wlr_seat_set_keyboard(wlr_seat, keyboard->wlr_keyboard);
wlr_seat_keyboard_notify_modifiers(wlr_seat, modifiers);
}
static bool keyboard_sync_physical_key(struct keyboard *keyboard, uint32_t key, bool state)
{
if (!keyboard->is_virtual || keyboard_is_from_input_method(keyboard)) {
return false;
}
if (key == KEY_CAPSLOCK) {
struct seat *seat = keyboard->seat;
struct keyboard *kb;
wl_list_for_each(kb, &seat->keyboards, link) {
if (keyboard_has_no_input(kb) || kb->is_virtual) {
continue;
}
keyboard_send_key(kb, key, state);
return true;
}
}
return false;
}
static void keyboard_handle_key(struct wl_listener *listener, void *data)
{
struct keyboard *keyboard = wl_container_of(listener, keyboard, key);
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard;
struct wlr_keyboard_key_event *event = data;
if (input_event_filter(keyboard->seat, NULL, INPUT_EVENT_KEYBOARD_KEY, event)) {
return;
}
struct keyboard_group *group = keyboard_group_from_wlr_keyboard(wlr_keyboard);
if (group) {
keyboard->seat->keyboard = keyboard;
}
uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_keyboard);
if (!keyboard_sync_physical_key(keyboard, event->keycode, event->state)) {
keyboard_feed_key(keyboard, event->keycode, event->state, event->time_msec, modifiers);
}
}
static void keyboard_handle_modifiers(struct wl_listener *listener, void *data)
{
struct keyboard *keyboard = wl_container_of(listener, keyboard, modifiers);
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard;
if (input_event_filter(keyboard->seat, NULL, INPUT_EVENT_KEYBOARD_MODIFIERS, data)) {
return;
}
keyboard_feed_modifiers(keyboard, &wlr_keyboard->modifiers);
}
static int keyboard_handle_repeat(void *data)
{
struct keyboard *keyboard = data;
if (keyboard->repeat.key > 0) {
if (keyboard->wlr_keyboard->repeat_info.rate > 0) {
// We queue the next event first, as the command might cancel it
if (wl_event_source_timer_update(keyboard->repeat.timer,
1000 / keyboard->wlr_keyboard->repeat_info.rate) < 0) {
kywc_log(KYWC_DEBUG, "Failed to update key repeat timer");
}
}
keyboard_feed_key(keyboard, keyboard->repeat.key, true, current_time_msec(),
wlr_keyboard_get_modifiers(keyboard->wlr_keyboard));
}
return 0;
}
struct keyboard *keyboard_create(struct seat *seat, struct wlr_keyboard *wlr_keyboard)
{
struct keyboard *keyboard = calloc(1, sizeof(*keyboard));
if (!keyboard) {
return NULL;
}
if (!wlr_keyboard) {
struct keyboard_group *group = keyboard_group_create();
keyboard->wlr_keyboard = &group->keyboard;
} else {
keyboard->wlr_keyboard = wlr_keyboard;
}
keyboard->is_virtual = !!wlr_keyboard;
keyboard->wlr_keyboard->data = keyboard;
/* insert new keyboard to seat keyboard list */
keyboard->seat = seat;
wl_list_insert(&seat->keyboards, &keyboard->link);
/* create timer for internal key repeat */
if (!keyboard->is_virtual) {
struct wl_event_loop *loop = wl_display_get_event_loop(seat->wlr_seat->display);
keyboard->repeat.timer = wl_event_loop_add_timer(loop, keyboard_handle_repeat, keyboard);
}
keyboard->key.notify = keyboard_handle_key;
wl_signal_add(&keyboard->wlr_keyboard->events.key, &keyboard->key);
keyboard->modifiers.notify = keyboard_handle_modifiers;
wl_signal_add(&keyboard->wlr_keyboard->events.modifiers, &keyboard->modifiers);
return keyboard;
}
void keyboard_add_input(struct seat *seat, struct input *input)
{
struct wlr_input_device *wlr_input = input->wlr_input;
struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(wlr_input);
/* virtual keyboard is not managed by group */
if (input->prop.is_virtual) {
keyboard_create(seat, wlr_keyboard);
return;
}
/* find a suitable group */
struct keyboard *keyboard, *empty_keyboard = NULL;
wl_list_for_each(keyboard, &seat->keyboards, link) {
if (keyboard->is_virtual) {
continue;
}
struct wlr_keyboard *dst_keyboard = keyboard->wlr_keyboard;
struct keyboard_group *group = keyboard_group_from_wlr_keyboard(dst_keyboard);
if (wl_list_empty(&group->devices)) {
empty_keyboard = keyboard;
continue;
}
if (keyboard_keymaps_match(wlr_keyboard, dst_keyboard) &&
wlr_keyboard->repeat_info.rate == dst_keyboard->repeat_info.rate &&
wlr_keyboard->repeat_info.delay == dst_keyboard->repeat_info.delay) {
keyboard_group_add_keyboard(group, wlr_keyboard);
return;
}
}
if (empty_keyboard) {
struct wlr_keyboard *dst_keyboard = empty_keyboard->wlr_keyboard;
if (!dst_keyboard->keymap || !keyboard_keymaps_match(wlr_keyboard, dst_keyboard)) {
wlr_keyboard_set_keymap(dst_keyboard, wlr_keyboard->keymap);
}
wlr_keyboard_set_repeat_info(dst_keyboard, wlr_keyboard->repeat_info.rate,
wlr_keyboard->repeat_info.delay);
struct keyboard_group *group = keyboard_group_from_wlr_keyboard(dst_keyboard);
keyboard_group_add_keyboard(group, wlr_keyboard);
return;
}
/* create a new keyboard group with keyboard configuration */
keyboard = keyboard_create(seat, NULL);
if (!keyboard) {
return;
}
struct keyboard_group *group = keyboard_group_from_wlr_keyboard(keyboard->wlr_keyboard);
wlr_keyboard_set_keymap(keyboard->wlr_keyboard, wlr_keyboard->keymap);
wlr_keyboard_set_repeat_info(keyboard->wlr_keyboard, wlr_keyboard->repeat_info.rate,
wlr_keyboard->repeat_info.delay);
keyboard_group_add_keyboard(group, wlr_keyboard);
}
void keyboard_destroy(struct keyboard *keyboard)
{
struct wlr_seat *wlr_seat = keyboard->seat->wlr_seat;
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard;
if (wlr_seat_get_keyboard(wlr_seat) == wlr_keyboard) {
wlr_seat_set_keyboard(wlr_seat, NULL);
}
wl_list_remove(&keyboard->link);
wl_list_remove(&keyboard->key.link);
wl_list_remove(&keyboard->modifiers.link);
if (!keyboard->is_virtual) {
struct keyboard_group *group = keyboard_group_from_wlr_keyboard(wlr_keyboard);
keyboard_group_destroy(group);
}
if (keyboard->repeat.timer) {
wl_event_source_remove(keyboard->repeat.timer);
}
if (keyboard->seat->keyboard == keyboard) {
keyboard->seat->keyboard = NULL;
}
free(keyboard);
}
void keyboard_remove_input(struct input *input)
{
struct wlr_input_device *wlr_input = input->wlr_input;
struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(wlr_input);
struct keyboard *keyboard;
if (input->prop.is_virtual) {
keyboard = wlr_keyboard->data;
keyboard_destroy(keyboard);
return;
}
struct keyboard_group *group = (struct keyboard_group *)wlr_keyboard->group;
/* may removed when input destroy at keyboard group */
if (group) {
keyboard_group_remove_keyboard(group, wlr_keyboard);
}
}
void keyboard_send_key(struct keyboard *keyboard, uint32_t key, bool pressed)
{
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard;
struct keyboard_group *group = keyboard_group_from_wlr_keyboard(wlr_keyboard);
if (!group) {
return;
}
struct wlr_keyboard_key_event wlr_event = {
.time_msec = current_time_msec(),
.keycode = key,
.update_state = true,
.state = pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
};
wlr_keyboard = keyboard_group_pick_keyboard(group);
wlr_keyboard_notify_key(wlr_keyboard, &wlr_event);
}
uint32_t keyboard_get_locks(struct keyboard *keyboard)
{
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard;
uint32_t locks = 0;
if (wlr_keyboard->modifiers.locked & WLR_MODIFIER_CAPS) {
locks |= 1 << INPUT_KEY_CAPSLOCK;
}
if (wlr_keyboard->modifiers.locked & WLR_MODIFIER_MOD2) {
locks |= 1 << INPUT_KEY_NUMLOCK;
}
struct keyboard_group *group = keyboard_group_from_wlr_keyboard(wlr_keyboard);
if (group && group->scroll_lock) {
locks |= 1 << INPUT_KEY_SCROLLLOCK;
}
return locks;
}
bool keyboard_has_no_input(struct keyboard *keyboard)
{
if (keyboard->is_virtual) {
return true;
}
struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard;
struct keyboard_group *group = keyboard_group_from_wlr_keyboard(wlr_keyboard);
return wl_list_empty(&group->devices);
}
bool keyboard_keymaps_match(struct wlr_keyboard *kb1, struct wlr_keyboard *kb2)
{
const char *km1 = kb1->keymap_string;
const char *km2 = kb2->keymap_string;
if (!km1 && !km2) {
return true;
}
if (!km1 || !km2) {
return false;
}
return strcmp(km1, km2) == 0;
}
static bool compare_string(const char *a, const char *b)
{
if (!a && !b) {
return false;
}
if (!a || !b) {
return true;
}
return strcmp(a, b) != 0;
}
bool keyboard_check_keymap_rules(struct keymap_rules *old, struct keymap_rules *new)
{
return compare_string(old->xkb_rules, new->xkb_rules) ||
compare_string(old->xkb_model, new->xkb_model) ||
compare_string(old->xkb_layout, new->xkb_layout) ||
compare_string(old->xkb_variant, new->xkb_variant) ||
compare_string(old->xkb_options, new->xkb_options);
}
struct xkb_keymap *keyboard_compile_keymap(struct keymap_rules *rules)
{
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV);
struct xkb_rule_names names = {
.layout = rules->xkb_layout,
.model = rules->xkb_model,
.options = rules->xkb_options,
.rules = rules->xkb_rules,
.variant = rules->xkb_variant,
};
struct xkb_keymap *keymap =
xkb_keymap_new_from_names(context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS);
xkb_context_unref(context);
return keymap;
}
kylin-wayland-compositor/src/input/selection.c 0000664 0001750 0001750 00000042220 15160461067 020544 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include
#include
#include
#if HAVE_XWAYLAND
#include
#endif
#include
#include "config.h"
#include "input/cursor.h"
#include "input_p.h"
#include "scene/surface.h"
#include "server.h"
#include "util/dbus.h"
#include "view/view.h"
struct selection_manager {
struct wl_listener new_seat;
struct wl_listener server_destroy;
};
/* selection per seat */
struct selection {
struct selection_manager *manager;
struct seat *seat;
struct wl_listener seat_destroy;
struct wl_listener request_start_drag;
struct wl_listener start_drag;
struct wl_listener destroy_drag;
struct wl_listener request_set_selection;
struct wl_listener request_set_primary_selection;
struct wlr_drag *drag;
/* only one drag_icon in seat at the same time */
struct wlr_drag_icon *drag_icon;
struct ky_scene_node *icon_node;
struct ky_scene_node *surface_node;
struct wl_listener drag_icon_map;
struct wl_listener drag_icon_unmap;
struct wl_listener drag_icon_commit;
struct wl_listener drag_icon_destroy;
struct wl_listener source_accepted;
struct wl_listener source_dnd_action;
#if HAVE_KDE_CLIPBOARD
struct wl_listener set_selection;
struct wl_listener set_primary_selection;
int clipboard_selection_pid;
int primary_selection_pid;
#endif
};
#if HAVE_KDE_CLIPBOARD
static const char *service_bus = "org.kde.KWin";
static const char *service_path = "/Clipboard";
static const char *service_interface = "org.kde.KWin.Clipboard";
// SD_BUS_METHOD("getClipboardSelectionPid", "", "i", get_clipboard_selection_pid, 0),
static int get_clipboard_selection_pid(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
struct seat *seat = input_manager_get_default_seat();
struct selection *selection = seat->selection;
return sd_bus_reply_method_return(msg, "i", selection->clipboard_selection_pid);
}
// SD_BUS_METHOD("getPrimarySelectionPid", "", "i", get_primary_selection_pid, 0),
static int get_primary_selection_pid(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error)
{
struct seat *seat = input_manager_get_default_seat();
struct selection *selection = seat->selection;
return sd_bus_reply_method_return(msg, "i", selection->primary_selection_pid);
}
// SD_BUS_PROPERTY("GetClipboardSelectionPid", "i", get_selection_pid, 0,
// SD_BUS_VTABLE_PROPERTY_CONST),
static int get_selection_pid(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct seat *seat = input_manager_get_default_seat();
struct selection *selection = seat->selection;
return sd_bus_message_append_basic(reply, 'i', &selection->clipboard_selection_pid);
}
// SD_BUS_PROPERTY("GetPrimarySelectionPid", "i", get_primary_pid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
static int get_primary_pid(sd_bus *bus, const char *path, const char *interface,
const char *property, sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error)
{
struct seat *seat = input_manager_get_default_seat();
struct selection *selection = seat->selection;
return sd_bus_message_append_basic(reply, 'i', &selection->primary_selection_pid);
}
static const sd_bus_vtable clipboard_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("getClipboardSelectionPid", "", "i", get_clipboard_selection_pid, 0),
SD_BUS_METHOD("getPrimarySelectionPid", "", "i", get_primary_selection_pid, 0),
SD_BUS_PROPERTY("GetClipboardSelectionPid", "i", get_selection_pid, 0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("GetPrimarySelectionPid", "i", get_primary_pid, 0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_SIGNAL("clipboardSelectionPidChanged", "i", 0),
SD_BUS_SIGNAL("primarySelectionPidChanged", "i", 0),
SD_BUS_VTABLE_END,
};
static pid_t get_pid_by_wlr_surface(struct wlr_surface *surface)
{
pid_t pid = 0;
if (!surface) {
return pid;
}
#if HAVE_XWAYLAND
struct wlr_xwayland_surface *xwayland = wlr_xwayland_surface_try_from_wlr_surface(surface);
if (xwayland) {
return xwayland->pid;
}
#endif
struct wl_client *client = wl_resource_get_client(surface->resource);
wl_client_get_credentials(client, &pid, NULL, NULL);
return pid;
}
static void send_selection_pid_changed(struct selection *selection, struct wlr_seat *seat,
bool is_primary)
{
pid_t pid = 0, current_pid = 0;
char *signal_name = NULL;
struct wlr_surface *focused_surface = seat->keyboard_state.focused_surface;
pid = get_pid_by_wlr_surface(focused_surface);
if (!is_primary && pid != selection->clipboard_selection_pid) {
current_pid = pid;
selection->clipboard_selection_pid = pid;
signal_name = "clipboardSelectionPidChanged";
} else if (is_primary && pid != selection->primary_selection_pid) {
current_pid = pid;
selection->primary_selection_pid = pid;
signal_name = "primarySelectionPidChanged";
}
if (signal_name) {
dbus_emit_signal(service_path, service_interface, signal_name, "i", current_pid);
}
}
static void handle_set_selection(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, set_selection);
struct wlr_seat *seat = data;
send_selection_pid_changed(selection, seat, false);
}
static void handle_set_primary_selection(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, set_primary_selection);
struct wlr_seat *seat = data;
send_selection_pid_changed(selection, seat, true);
}
#endif
static void handle_drag_icon_map(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, drag_icon_map);
ky_scene_node_set_enabled(selection->icon_node, true);
}
static void handle_drag_icon_unmap(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, drag_icon_unmap);
ky_scene_node_set_enabled(selection->icon_node, false);
}
static void handle_drag_icon_commit(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, drag_icon_commit);
struct wlr_surface *surface = selection->drag_icon->surface;
ky_scene_node_set_position(selection->surface_node,
selection->surface_node->x + surface->current.dx,
selection->surface_node->y + surface->current.dy);
}
static void handle_drag_icon_destroy(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, drag_icon_destroy);
wl_list_remove(&selection->drag_icon_map.link);
wl_list_remove(&selection->drag_icon_unmap.link);
wl_list_remove(&selection->drag_icon_commit.link);
wl_list_remove(&selection->drag_icon_destroy.link);
ky_scene_node_destroy(selection->icon_node);
selection->icon_node = NULL;
}
static void handle_request_set_selection(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, request_set_selection);
struct wlr_seat *seat = selection->seat->wlr_seat;
struct wlr_seat_request_set_selection_event *event = data;
wlr_seat_set_selection(seat, event->source, event->serial);
}
static void handle_request_start_drag(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, request_start_drag);
struct wlr_seat *wlr_seat = selection->seat->wlr_seat;
struct wlr_seat_request_start_drag_event *event = data;
if (wlr_seat_validate_pointer_grab_serial(wlr_seat, event->origin, event->serial)) {
wlr_seat_start_pointer_drag(wlr_seat, event->drag, event->serial);
return;
}
struct wlr_touch_point *point;
if (wlr_seat_validate_touch_grab_serial(wlr_seat, event->origin, event->serial, &point)) {
wlr_seat_start_touch_drag(wlr_seat, event->drag, event->serial, point);
return;
}
/* workaround to simulate pointer drag */
if (tablet_has_implicit_grab(selection->seat)) {
wlr_seat->pointer_state.grab_button = BTN_LEFT;
selection->seat->cursor->tablet_tool_tip_simulation_pointer = true;
wlr_seat_start_pointer_drag(wlr_seat, event->drag, event->serial);
return;
}
wlr_data_source_destroy(event->drag->source);
}
static void update_drag_cursor(struct selection *selection)
{
struct wlr_data_source *source = selection->drag->source;
struct cursor *cursor = selection->seat->cursor;
cursor_lock_image(cursor, false);
if (source && source->accepted) {
switch (source->current_dnd_action) {
case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE:
cursor_set_image(cursor, CURSOR_GRABBING);
break;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY:
cursor_set_image(cursor, CURSOR_COPY);
break;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE:
cursor_set_image(cursor, CURSOR_MOVE);
break;
case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK:
cursor_set_image(cursor, CURSOR_GRABBING);
break;
}
} else {
cursor_set_image(cursor, CURSOR_NOT_ALLOWED);
}
cursor_lock_image(cursor, true);
}
static void handle_source_accepted(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, source_accepted);
update_drag_cursor(selection);
}
static void handle_source_dnd_action(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, source_dnd_action);
update_drag_cursor(selection);
}
static void handle_start_drag(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, start_drag);
struct wlr_drag *wlr_drag = data;
struct wlr_drag_icon *drag_icon = wlr_drag->icon;
selection->drag = wlr_drag;
selection->drag_icon = drag_icon;
wl_signal_add(&wlr_drag->events.destroy, &selection->destroy_drag);
cursor_set_image(selection->seat->cursor, CURSOR_DEFAULT);
cursor_lock_image(selection->seat->cursor, true);
// selection->source_accepted.notify = handle_source_accepted;
// wl_signal_add(&selection->drag->source->events.accepted, &selection->source_accepted);
// selection->source_dnd_action.notify = handle_source_dnd_action;
// wl_signal_add(&selection->drag->source->events.dnd_action,
// &selection->source_dnd_action);
/* drag icon may be NULL */
if (!drag_icon) {
kywc_log(KYWC_DEBUG, "Started drag but not set a drag icon");
return;
}
struct view_layer *layer = view_manager_get_layer(LAYER_ON_SCREEN_DISPLAY, false);
struct ky_scene_tree *tree = ky_scene_tree_create(layer->tree);
struct ky_scene_tree *surface_tree = ky_scene_subsurface_tree_create(tree, drag_icon->surface);
selection->icon_node = &tree->node;
selection->surface_node = &surface_tree->node;
ky_scene_node_set_position(selection->icon_node, selection->seat->cursor->lx,
selection->seat->cursor->ly);
ky_scene_node_set_enabled(selection->icon_node, drag_icon->surface->mapped);
selection->drag_icon_map.notify = handle_drag_icon_map;
wl_signal_add(&drag_icon->surface->events.map, &selection->drag_icon_map);
selection->drag_icon_unmap.notify = handle_drag_icon_unmap;
wl_signal_add(&drag_icon->surface->events.unmap, &selection->drag_icon_unmap);
selection->drag_icon_commit.notify = handle_drag_icon_commit;
wl_signal_add(&drag_icon->surface->events.commit, &selection->drag_icon_commit);
selection->drag_icon_destroy.notify = handle_drag_icon_destroy;
wl_signal_add(&drag_icon->events.destroy, &selection->drag_icon_destroy);
}
static void handle_destroy_drag(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, destroy_drag);
wl_list_remove(&selection->destroy_drag.link);
// wl_list_remove(&selection->source_accepted.link);
// wl_list_remove(&selection->source_dnd_action.link);
selection->drag = NULL;
cursor_lock_image(selection->seat->cursor, false);
cursor_set_image(selection->seat->cursor, CURSOR_DEFAULT);
}
static void handle_request_set_primary_selection(struct wl_listener *listener, void *data)
{
struct selection *selection =
wl_container_of(listener, selection, request_set_primary_selection);
struct wlr_seat *seat = selection->seat->wlr_seat;
struct wlr_seat_request_set_primary_selection_event *event = data;
wlr_seat_set_primary_selection(seat, event->source, event->serial);
}
static void handle_seat_destroy(struct wl_listener *listener, void *data)
{
struct selection *selection = wl_container_of(listener, selection, seat_destroy);
wl_list_remove(&selection->request_start_drag.link);
wl_list_remove(&selection->start_drag.link);
#if HAVE_KDE_CLIPBOARD
wl_list_remove(&selection->set_selection.link);
wl_list_remove(&selection->set_primary_selection.link);
#endif
wl_list_remove(&selection->request_set_selection.link);
wl_list_remove(&selection->request_set_primary_selection.link);
wl_list_remove(&selection->seat_destroy.link);
free(selection);
}
static void handle_new_seat(struct wl_listener *listener, void *data)
{
struct selection *selection = calloc(1, sizeof(*selection));
if (!selection) {
return;
}
struct selection_manager *manager = wl_container_of(listener, manager, new_seat);
selection->manager = manager;
struct seat *seat = data;
selection->seat = seat;
seat->selection = selection;
selection->seat_destroy.notify = handle_seat_destroy;
wl_signal_add(&seat->events.destroy, &selection->seat_destroy);
selection->request_start_drag.notify = handle_request_start_drag;
wl_signal_add(&seat->wlr_seat->events.request_start_drag, &selection->request_start_drag);
selection->start_drag.notify = handle_start_drag;
wl_signal_add(&seat->wlr_seat->events.start_drag, &selection->start_drag);
selection->destroy_drag.notify = handle_destroy_drag;
wl_list_init(&selection->destroy_drag.link);
#if HAVE_KDE_CLIPBOARD
selection->set_selection.notify = handle_set_selection;
wl_signal_add(&seat->wlr_seat->events.set_selection, &selection->set_selection);
selection->set_primary_selection.notify = handle_set_primary_selection;
wl_signal_add(&seat->wlr_seat->events.set_primary_selection, &selection->set_primary_selection);
#endif
selection->request_set_selection.notify = handle_request_set_selection;
wl_signal_add(&seat->wlr_seat->events.request_set_selection, &selection->request_set_selection);
selection->request_set_primary_selection.notify = handle_request_set_primary_selection;
wl_signal_add(&seat->wlr_seat->events.request_set_primary_selection,
&selection->request_set_primary_selection);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct selection_manager *manager = wl_container_of(listener, manager, server_destroy);
wl_list_remove(&manager->server_destroy.link);
wl_list_remove(&manager->new_seat.link);
free(manager);
}
bool selection_manager_create(struct input_manager *input_manager)
{
struct selection_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
#if HAVE_KDE_CLIPBOARD
if (!dbus_register_object(service_bus, service_path, service_interface, clipboard_vtable,
manager)) {
kywc_log(KYWC_WARN, "Failed to register org.kde.KWin.Clipboard");
}
#endif
wlr_data_device_manager_create(input_manager->server->display);
wlr_data_control_manager_v1_create(input_manager->server->display);
wlr_primary_selection_v1_device_manager_create(input_manager->server->display);
toplevel_drag_manager_create(input_manager->server);
manager->new_seat.notify = handle_new_seat;
wl_signal_add(&input_manager->events.new_seat, &manager->new_seat);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(input_manager->server, &manager->server_destroy);
return true;
}
void selection_handle_cursor_move(struct seat *seat, int lx, int ly)
{
if (!seat->selection || !seat->selection->drag) {
return;
}
if (toplevel_drag_move(seat->selection->drag->source, lx, ly)) {
return;
}
/* update dnd icon if support */
if (seat->selection->icon_node) {
ky_scene_node_set_position(seat->selection->icon_node, lx, ly);
}
}
bool selection_is_dragging(struct seat *seat)
{
return seat->selection && seat->selection->drag;
}
kylin-wayland-compositor/src/input/cursor.c 0000664 0001750 0001750 00000147472 15160461067 020113 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "input/event.h"
#include "input_p.h"
#include "scene/surface.h"
#include "server.h"
#include "util/time.h"
#include "xwayland.h"
/* cursor images used in compositor */
static char *cursor_image[] = {
"none", "default", "context-menu", "help", "pointer", "progress",
"wait", "cell", "crosshair", "text", "vertical-text", "alias",
"copy", "move", "no-drop", "not-allowed", "grab", "grabbing",
"e-resize", "n-resize", "ne-resize", "nw-resize", "s-resize", "se-resize",
"sw-resize", "w-resize", "ew-resize", "ns-resize", "nesw-resize", "nwse-resize",
"col-resize", "row-resize", "all-scroll", "zoom-in", "zoom-out",
};
static bool cursor_set_node(struct cursor_node *cursor_node, struct ky_scene_node *hover)
{
if (hover == cursor_node->node) {
return false;
}
if (cursor_node->node) {
wl_list_remove(&cursor_node->destroy.link);
}
if (hover) {
wl_signal_add(&hover->events.destroy, &cursor_node->destroy);
}
cursor_node->node = hover;
return true;
}
static bool cursor_update_node(struct cursor *cursor, bool click)
{
struct seat *seat = cursor->seat;
/* find node below the cursor */
struct ky_scene_node *hover =
ky_scene_node_at(&seat->scene->tree.node, cursor->lx, cursor->ly, &cursor->sx, &cursor->sy);
/* update cursor hover node */
if (!click) {
return cursor_set_node(&cursor->hover, hover);
}
/* update cursor focus node */
return cursor_set_node(&cursor->focus, hover);
}
static void _cursor_feed_motion(struct cursor *cursor, uint32_t time)
{
struct seat *seat = cursor->seat;
struct ky_scene_node *old_hover = cursor->hover.node;
bool changed = cursor_update_node(cursor, false);
bool left_button_pressed =
LEFT_BUTTON_PRESSED(cursor->last_click_button, cursor->last_click_pressed);
/* if hold press moving but not dragging */
if (left_button_pressed && cursor->focus.node && cursor->focus.node != cursor->hover.node &&
!selection_is_dragging(seat)) {
struct input_event_node *inode = input_event_node_from_node(cursor->focus.node);
if (inode && inode->impl->hover) {
cursor->hold_mode = inode->impl->hover(seat, cursor->focus.node, cursor->lx, cursor->ly,
time, false, true, inode->data);
}
if (cursor->hold_mode) {
return;
}
}
/* mark hold_mode to false, hover to node again */
cursor->hold_mode = false;
/* cursor has moved to another node */
struct input_event_node *inode = input_event_node_from_node(cursor->hover.node);
if (changed && old_hover) {
struct input_event_node *old_inode = input_event_node_from_node(old_hover);
if (old_inode && old_inode->impl->leave) {
bool leave = input_event_node_root(old_inode) != input_event_node_root(inode);
old_inode->impl->leave(seat, old_hover, leave, old_inode->data);
}
}
/* hover current node */
if (inode && inode->impl->hover) {
inode->impl->hover(seat, cursor->hover.node, cursor->sx, cursor->sy, time, changed, false,
inode->data);
}
selection_handle_cursor_move(seat, cursor->lx, cursor->ly);
if (!cursor->hover.node) {
/* once no node found under the cursor, restore cursor to default */
cursor_set_image(cursor, CURSOR_DEFAULT);
/* clear pointer focus if hover changed to null */
if (changed) {
seat_notify_leave(seat, NULL);
}
}
}
/* return true if pointer is locked */
static bool cursor_apply_constraint(struct cursor *cursor, struct wlr_input_device *device,
double *dx, double *dy);
void cursor_feed_motion(struct cursor *cursor, uint32_t time, struct wlr_input_device *device,
double dx, double dy, double dx_unaccel, double dy_unaccel)
{
wlr_relative_pointer_manager_v1_send_relative_motion(
cursor->seat->manager->relative_pointer, cursor->seat->wlr_seat, (uint64_t)time * 1000, dx,
dy, dx_unaccel, dy_unaccel);
if (cursor_apply_constraint(cursor, device, &dx, &dy)) {
return;
}
cursor_move(cursor, device, dx, dy, true, false);
struct seat_pointer_grab *pointer_grab = cursor->seat->pointer_grab;
if (pointer_grab && pointer_grab->interface->motion &&
pointer_grab->interface->motion(pointer_grab, time, cursor->lx, cursor->ly)) {
return;
}
_cursor_feed_motion(cursor, time);
}
static void cursor_motion_absolute(struct cursor *cursor, uint32_t time,
struct wlr_input_device *dev, double x, double y)
{
double lx, ly;
wlr_cursor_absolute_to_layout_coords(cursor->wlr_cursor, dev, x, y, &lx, &ly);
double dx = lx - cursor->lx;
double dy = ly - cursor->ly;
cursor_feed_motion(cursor, time, dev, dx, dy, dx, dy);
}
static void cursor_feed_fake_motion(struct cursor *cursor, bool leave)
{
/* force leave current hover node, then re-hover it */
if (leave && cursor->hover.node) {
struct input_event_node *inode = input_event_node_from_node(cursor->hover.node);
if (inode && inode->impl->leave) {
inode->impl->leave(cursor->seat, cursor->hover.node, false, inode->data);
}
}
/* skip motion when has grab */
if (cursor->seat->pointer_grab && cursor->seat->pointer_grab->interface->motion) {
return;
}
_cursor_feed_motion(cursor, current_time_msec());
if (leave) {
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
}
}
void cursor_feed_button(struct cursor *cursor, uint32_t button, bool pressed, uint32_t time,
uint32_t double_click_time)
{
struct seat *seat = cursor->seat;
if (seat->pointer_grab && seat->pointer_grab->interface->button &&
seat->pointer_grab->interface->button(seat->pointer_grab, time, button, pressed)) {
return;
}
bool last_is_pressed = cursor->last_click_pressed;
uint32_t last_button = cursor->last_click_button;
cursor->last_click_pressed = pressed;
/* current focus node */
struct ky_scene_node *old_focus = cursor->focus.node;
bool changed = cursor_update_node(cursor, true);
if (wlr_seat_pointer_has_grab(seat->wlr_seat)) {
seat_notify_button(seat, time, button, pressed);
return;
}
/* old focus node and view */
struct input_event_node *old_inode = input_event_node_from_node(old_focus);
struct input_event_node *inode = input_event_node_from_node(cursor->focus.node);
/* exit hold mode if any button clicked */
if (cursor->hold_mode) {
/* send button release to last focus node */
if (old_inode && old_inode->impl->click) {
old_inode->impl->click(seat, old_focus, BTN_LEFT, false, time, CLICK_STATE_NONE,
old_inode->data);
}
/* leave focus node, otherwise wrong cursor image */
if (old_inode && old_inode->impl->leave) {
bool leave = input_event_node_root(old_inode) != input_event_node_root(inode);
old_inode->impl->leave(seat, old_focus, leave, old_inode->data);
}
if (inode && inode->impl->hover) {
inode->impl->hover(seat, cursor->focus.node, cursor->sx, cursor->sy, time, true, false,
inode->data);
} else {
cursor_set_image(cursor, CURSOR_DEFAULT);
}
cursor->hold_mode = false;
return;
}
/* no pointer motion or hover node was destroyed */
if (!cursor->hover.node && cursor->focus.node && inode) {
inode->impl->hover(seat, cursor->focus.node, cursor->sx, cursor->sy, time, true, false,
inode->data);
}
/* send a button released event to old focus node */
if (old_focus && changed && !pressed && last_is_pressed) {
kywc_log(KYWC_DEBUG, "Release button %d in %p", last_button, old_focus);
if (old_inode && old_inode->impl->click) {
old_inode->impl->click(seat, old_focus, last_button, false, time,
CLICK_STATE_FOCUS_LOST, old_inode->data);
}
/* fix cursor image sometimes */
if (!seat_is_dragging(seat)) {
cursor_feed_fake_motion(cursor, false);
}
return;
}
bool double_click = false;
if (pressed) {
if (!changed && button == cursor->last_click_button &&
time - cursor->last_click_time < double_click_time) {
double_click = true;
}
/* reset after a double click */
cursor->last_click_time = double_click ? 0 : time;
cursor->last_click_button = button;
}
if (inode && inode->impl->click) {
inode->impl->click(seat, cursor->focus.node, button, pressed, time,
double_click ? CLICK_STATE_DOUBLE : CLICK_STATE_NONE, inode->data);
}
/* update surface coord if surface size changed when click, like maximize */
if (cursor->hover.node == cursor->focus.node && !seat_is_dragging(seat)) {
cursor_feed_fake_motion(cursor, false);
}
if (!cursor->focus.node) {
/* clear keyboard focus */
seat_focus_surface(seat, NULL);
}
}
static void cursor_handle_motion(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, motion);
struct wlr_pointer_motion_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_POINTER_MOTION, event)) {
return;
}
cursor_set_hidden(cursor, false);
cursor_feed_motion(cursor, event->time_msec, &event->pointer->base, event->delta_x,
event->delta_y, event->unaccel_dx, event->unaccel_dy);
}
static void cursor_handle_motion_absolute(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, motion_absolute);
struct wlr_pointer_motion_absolute_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_POINTER_MOTION_ABSOLUTE, event)) {
return;
}
cursor_set_hidden(cursor, false);
cursor_motion_absolute(cursor, event->time_msec, &event->pointer->base, event->x, event->y);
}
static void cursor_handle_button(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, button);
struct wlr_pointer_button_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_POINTER_BUTTON, event)) {
return;
}
cursor_set_hidden(cursor, false);
cursor_feed_button(cursor, event->button, event->state == WL_POINTER_BUTTON_STATE_PRESSED,
event->time_msec, input->state.double_click_time);
}
void cursor_feed_axis(struct cursor *cursor, uint32_t orientation, uint32_t source, double delta,
int32_t delta_discrete, int32_t relative_direction, uint32_t time)
{
struct seat *seat = cursor->seat;
if (seat->pointer_grab && seat->pointer_grab->interface->axis &&
seat->pointer_grab->interface->axis(
seat->pointer_grab, time, orientation == WL_POINTER_AXIS_VERTICAL_SCROLL, delta)) {
return;
}
/* Notify the client with pointer focus of the axis event. */
wlr_seat_pointer_notify_axis(seat->wlr_seat, time, orientation, delta, delta_discrete, source,
relative_direction);
}
static void cursor_handle_axis(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, axis);
struct wlr_pointer_axis_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_POINTER_AXIS, event)) {
return;
}
/* workaround to fix zwcada2023 crash */
static uint32_t time_tmp = 0;
if (time_tmp == event->time_msec) {
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
}
time_tmp = event->time_msec;
cursor_set_hidden(cursor, false);
cursor_feed_axis(cursor, event->orientation, event->source,
input->state.scroll_factor * event->delta,
roundf(input->state.scroll_factor * event->delta_discrete),
event->relative_direction, event->time_msec);
}
static void cursor_handle_frame(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, frame);
if (input_event_filter(cursor->seat, NULL, INPUT_EVENT_POINTER_FRAME, data)) {
return;
}
/* only send to client when pointer grab frame return false */
if (cursor->seat->pointer_grab &&
(!cursor->seat->pointer_grab->interface->frame ||
cursor->seat->pointer_grab->interface->frame(cursor->seat->pointer_grab))) {
return;
}
/* Notify the client with pointer focus of the frame event. */
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
}
static void cursor_handle_tablet_tool_axis(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, tablet_tool_axis);
struct wlr_tablet_tool_axis_event *event = data;
struct input *input = input_from_wlr_input(&event->tablet->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_TABLET_TOOL_AXIS, event)) {
return;
}
cursor_set_hidden(cursor, false);
/* force to pointer when move and resize */
if (cursor->seat->pointer_grab) {
cursor->tablet_tool_tip_simulation_pointer = true;
}
bool change_x = event->updated_axes & WLR_TABLET_TOOL_AXIS_X;
bool change_y = event->updated_axes & WLR_TABLET_TOOL_AXIS_Y;
if (cursor->tablet_tool_tip_simulation_pointer && (change_x || change_y)) {
if (event->tool->type == WLR_TABLET_TOOL_TYPE_LENS ||
event->tool->type == WLR_TABLET_TOOL_TYPE_MOUSE) {
cursor_feed_motion(cursor, event->time_msec, &event->tablet->base, event->dx, event->dy,
event->dx, event->dy);
} else {
cursor_motion_absolute(cursor, event->time_msec, &event->tablet->base,
change_x ? event->x : NAN, change_y ? event->y : NAN);
}
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
return;
}
if (!cursor->tablet_tool_tip_simulation_pointer) {
tablet_handle_tool_axis(event);
}
}
static void cursor_handle_tablet_tool_proximity(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, tablet_tool_proximity);
struct wlr_tablet_tool_proximity_event *event = data;
struct input *input = input_from_wlr_input(&event->tablet->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_TABLET_TOOL_PROXIMITY, event)) {
return;
}
cursor_set_hidden(cursor, false);
if (!cursor->tablet_tool_tip_simulation_pointer && tablet_handle_tool_proximity(event)) {
return;
}
if (event->state == WLR_TABLET_TOOL_PROXIMITY_OUT) {
return;
}
cursor_motion_absolute(cursor, event->time_msec, &event->tablet->base, event->x, event->y);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
}
static void cursor_handle_tablet_tool_tip(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, tablet_tool_tip);
struct wlr_tablet_tool_tip_event *event = data;
struct input *input = input_from_wlr_input(&event->tablet->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_TABLET_TOOL_TIP, event)) {
return;
}
cursor->tablet_tool_tip_down = event->state == WLR_TABLET_TOOL_TIP_DOWN;
cursor_set_hidden(cursor, false);
/* workaround: tablet simulate pointer drag to fix peony drag */
cursor_motion_absolute(cursor, event->time_msec, &event->tablet->base, event->x, event->y);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
if (cursor->tablet_tool_tip_simulation_pointer && event->state == WLR_TABLET_TOOL_TIP_UP) {
cursor->tablet_tool_tip_simulation_pointer = false;
cursor_feed_button(cursor, BTN_LEFT, false, event->time_msec,
input->state.double_click_time);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
/* workaround to send a tool-tip up */
tablet_handle_tool_tip(event);
return;
}
if (seat_is_dragging(cursor->seat)) {
return;
}
if (tablet_handle_tool_tip(event)) {
return;
}
if (event->state == WLR_TABLET_TOOL_TIP_UP) {
return;
}
cursor->tablet_tool_tip_simulation_pointer = true;
cursor_feed_button(cursor, BTN_LEFT, true, event->time_msec, input->state.double_click_time);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
}
static void cursor_handle_tablet_tool_button(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, tablet_tool_button);
struct wlr_tablet_tool_button_event *event = data;
struct input *input = input_from_wlr_input(&event->tablet->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_TABLET_TOOL_BUTTON, event)) {
return;
}
if (seat_is_dragging(cursor->seat)) {
return;
}
cursor_set_hidden(cursor, false);
if (cursor->tablet_tool_buttons > 0 && cursor->tablet_tool_button_simulation_pointer) {
struct input *input = input_from_wlr_input(&event->tablet->base);
cursor_feed_button(cursor, BTN_RIGHT, event->state == WLR_BUTTON_PRESSED, event->time_msec,
input->state.double_click_time);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
} else if (tablet_handle_tool_button(event)) {
cursor->tablet_tool_button_simulation_pointer = false;
} else {
cursor->tablet_tool_button_simulation_pointer = true;
}
switch (event->state) {
case WLR_BUTTON_PRESSED:
cursor->tablet_tool_buttons++;
break;
case WLR_BUTTON_RELEASED:
if (cursor->tablet_tool_buttons == 0) {
kywc_log(KYWC_ERROR, "Inconsistent tablet tool button events");
} else {
cursor->tablet_tool_buttons--;
}
break;
}
}
static void cursor_handle_touch_up(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, touch_up);
struct wlr_touch_up_event *event = data;
struct input *input = input_from_wlr_input(&event->touch->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_TOUCH_UP, event)) {
return;
}
touch_handle_up(event, !cursor->touch_simulation_pointer);
if (cursor->touch_simulation_pointer && cursor->pointer_touch_id == event->touch_id) {
cursor->pointer_touch_up = true;
struct input *input = input_from_wlr_input(&event->touch->base);
cursor_feed_button(cursor, BTN_LEFT, false, event->time_msec,
input->state.double_click_time);
}
cursor->pointer_touch_id = -1;
}
static void cursor_handle_touch_down(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, touch_down);
struct wlr_touch_down_event *event = data;
struct input *input = input_from_wlr_input(&event->touch->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_TOUCH_DOWN, event)) {
return;
}
cursor_set_hidden(cursor, true);
/* workaround to fix peony drag icon position */
cursor_motion_absolute(cursor, event->time_msec, &event->touch->base, event->x, event->y);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
cursor->pointer_touch_id = event->touch_id;
if (touch_handle_down(event)) {
return;
}
cursor->touch_simulation_pointer = true;
cursor_feed_button(cursor, BTN_LEFT, true, event->time_msec, input->state.double_click_time);
}
static void cursor_handle_touch_motion(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, touch_motion);
struct wlr_touch_motion_event *event = data;
struct input *input = input_from_wlr_input(&event->touch->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_TOUCH_MOTION, event)) {
return;
}
touch_handle_motion(event, !cursor->touch_simulation_pointer);
if ((cursor->seat->pointer_grab || cursor->touch_simulation_pointer) &&
cursor->pointer_touch_id == event->touch_id) {
cursor_motion_absolute(cursor, event->time_msec, &event->touch->base, event->x, event->y);
}
}
static void cursor_handle_touch_cancel(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, touch_cancel);
struct wlr_touch_cancel_event *event = data;
struct input *input = input_from_wlr_input(&event->touch->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_TOUCH_CANCEL, event)) {
return;
}
touch_handle_cancel(event, !cursor->touch_simulation_pointer);
if (cursor->touch_simulation_pointer && cursor->pointer_touch_id == event->touch_id) {
cursor->pointer_touch_up = true;
struct input *input = input_from_wlr_input(&event->touch->base);
cursor_feed_button(cursor, BTN_LEFT, false, event->time_msec,
input->state.double_click_time);
}
cursor->pointer_touch_id = -1;
}
static void cursor_handle_touch_frame(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, touch_frame);
if (input_event_filter(cursor->seat, NULL, INPUT_EVENT_TOUCH_FRAME, data)) {
return;
}
if (cursor->touch_simulation_pointer) {
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
if (cursor->pointer_touch_up) {
cursor->pointer_touch_up = false;
cursor->touch_simulation_pointer = false;
}
return;
}
wlr_seat_touch_notify_frame(cursor->seat->wlr_seat);
}
static void cursor_handle_swipe_begin(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, swipe_begin);
struct wlr_pointer_swipe_begin_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_GESTURE_SWIPE_BEGIN, event)) {
return;
}
gesture_state_begin(&cursor->gestures, GESTURE_TYPE_SWIPE, GESTURE_DEVICE_TOUCHPAD,
GESTURE_EDGE_NONE, event->fingers);
wlr_pointer_gestures_v1_send_swipe_begin(cursor->seat->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
}
static void cursor_handle_swipe_update(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, swipe_update);
struct wlr_pointer_swipe_update_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_GESTURE_SWIPE_UPDATE, event)) {
return;
}
gesture_state_update(&cursor->gestures, GESTURE_TYPE_SWIPE, GESTURE_DEVICE_TOUCHPAD, event->dx,
event->dy, NAN, NAN);
wlr_pointer_gestures_v1_send_swipe_update(cursor->seat->pointer_gestures,
cursor->seat->wlr_seat, event->time_msec, event->dx,
event->dy);
}
static void cursor_handle_swipe_end(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, swipe_end);
struct wlr_pointer_swipe_end_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_GESTURE_SWIPE_END, event)) {
return;
}
bool handled = gesture_state_end(&cursor->gestures, GESTURE_TYPE_SWIPE, GESTURE_DEVICE_TOUCHPAD,
event->cancelled);
/* tell client the gesture is cancelled if gesture handled by compositor */
wlr_pointer_gestures_v1_send_swipe_end(cursor->seat->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled || handled);
}
static void cursor_handle_pinch_begin(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, pinch_begin);
struct wlr_pointer_pinch_begin_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_GESTURE_PINCH_BEGIN, event)) {
return;
}
gesture_state_begin(&cursor->gestures, GESTURE_TYPE_PINCH, GESTURE_DEVICE_TOUCHPAD,
GESTURE_EDGE_NONE, event->fingers);
wlr_pointer_gestures_v1_send_pinch_begin(cursor->seat->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
}
static void cursor_handle_pinch_update(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, pinch_update);
struct wlr_pointer_pinch_update_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_GESTURE_PINCH_UPDATE, event)) {
return;
}
gesture_state_update(&cursor->gestures, GESTURE_TYPE_PINCH, GESTURE_DEVICE_TOUCHPAD, event->dx,
event->dy, event->scale, event->rotation);
wlr_pointer_gestures_v1_send_pinch_update(cursor->seat->pointer_gestures,
cursor->seat->wlr_seat, event->time_msec, event->dx,
event->dy, event->scale, event->rotation);
}
static void cursor_handle_pinch_end(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, pinch_end);
struct wlr_pointer_pinch_end_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_GESTURE_PINCH_END, event)) {
return;
}
bool handled = gesture_state_end(&cursor->gestures, GESTURE_TYPE_PINCH, GESTURE_DEVICE_TOUCHPAD,
event->cancelled);
wlr_pointer_gestures_v1_send_pinch_end(cursor->seat->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled || handled);
}
static void cursor_handle_hold_begin(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, hold_begin);
struct wlr_pointer_hold_begin_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_GESTURE_HOLD_BEGIN, event)) {
return;
}
gesture_state_begin(&cursor->gestures, GESTURE_TYPE_HOLD, GESTURE_DEVICE_TOUCHPAD,
GESTURE_EDGE_NONE, event->fingers);
wlr_pointer_gestures_v1_send_hold_begin(cursor->seat->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
}
static void cursor_handle_hold_end(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, hold_end);
struct wlr_pointer_hold_end_event *event = data;
struct input *input = input_from_wlr_input(&event->pointer->base);
if (input_event_filter(cursor->seat, input, INPUT_EVENT_GESTURE_HOLD_END, event)) {
return;
}
bool handled = gesture_state_end(&cursor->gestures, GESTURE_TYPE_HOLD, GESTURE_DEVICE_TOUCHPAD,
event->cancelled);
wlr_pointer_gestures_v1_send_hold_end(cursor->seat->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled || handled);
}
static void cursor_handle_surface_client_commit(struct wl_listener *listener, void *data)
{
float scale = xwayland_get_scale();
if (scale == 1.0) {
return;
}
struct cursor *cursor = wl_container_of(listener, cursor, surface_client_commit);
struct wlr_surface_state *pending = &cursor->surface->pending;
pending->width = xwayland_unscale(pending->width);
pending->height = xwayland_unscale(pending->height);
if (pending->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) {
wlr_region_scale(&pending->surface_damage, &pending->surface_damage, 1.0 / scale);
}
if (pending->committed & WLR_SURFACE_STATE_OFFSET) {
pending->dx = xwayland_unscale(pending->dx);
pending->dy = xwayland_unscale(pending->dy);
}
}
static void cursor_handle_surface_destroy(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, surface_destroy);
wl_list_remove(&cursor->surface_client_commit.link);
wl_list_remove(&cursor->surface_destroy.link);
wl_list_init(&cursor->surface_client_commit.link);
wl_list_init(&cursor->surface_destroy.link);
cursor->surface = NULL;
}
void cursor_set_surface(struct cursor *cursor, struct wlr_surface *surface, int32_t hotspot_x,
int32_t hotspot_y, struct wl_client *client)
{
/* unscale cursor surface from xwayland */
if (xwayland_check_client(client)) {
if (surface) {
hotspot_x = xwayland_unscale(hotspot_x);
hotspot_y = xwayland_unscale(hotspot_y);
}
if (cursor->surface != surface) {
if (cursor->surface) {
cursor_handle_surface_destroy(&cursor->surface_destroy, NULL);
}
if (surface) {
cursor->surface = surface;
wl_signal_add(&surface->events.client_commit, &cursor->surface_client_commit);
wl_signal_add(&surface->events.destroy, &cursor->surface_destroy);
}
}
}
/* use this to filter cursor image */
cursor->client_requested = true;
wlr_cursor_set_surface(cursor->wlr_cursor, surface, hotspot_x, hotspot_y);
}
static void cursor_handle_request_set_cursor(struct wl_listener *listener, void *data)
{
struct cursor *cursor = wl_container_of(listener, cursor, request_set_cursor);
struct wlr_seat_pointer_request_set_cursor_event *event = data;
struct wlr_seat_client *focused_client = cursor->seat->wlr_seat->pointer_state.focused_client;
struct seat_pointer_grab *grab = cursor->seat->pointer_grab;
if (cursor->image_locks > 0 || cursor->hidden) {
return;
}
if (grab || focused_client != event->seat_client) {
return;
}
cursor_set_surface(cursor, event->surface, event->hotspot_x, event->hotspot_y,
event->seat_client->client);
}
static void cursor_node_handle_destroy(struct wl_listener *listener, void *data)
{
struct cursor_node *cursor_node = wl_container_of(listener, cursor_node, destroy);
wl_list_remove(&cursor_node->destroy.link);
cursor_node->node = NULL;
}
void cursor_set_xcursor_manager(struct cursor *cursor, const char *theme, uint32_t size, bool saved)
{
bool need_set = !cursor->xcursor_manager;
if (!need_set) {
bool same_theme = (!cursor->xcursor_manager->name && !theme) ||
(theme && cursor->xcursor_manager->name &&
strcmp(theme, cursor->xcursor_manager->name) == 0);
need_set = !same_theme || cursor->xcursor_manager->size != size;
}
if (!need_set) {
return;
}
/* clear wlr_cursor state */
wlr_cursor_unset_image(cursor->wlr_cursor);
/* destroy the prev one, NULL is ok */
wlr_xcursor_manager_destroy(cursor->xcursor_manager);
cursor->xcursor_manager = wlr_xcursor_manager_create(theme, size);
/* apply the new configuration */
cursor_rebase(cursor);
if (!saved) {
return;
}
free((void *)cursor->seat->state.cursor_theme);
cursor->seat->state.cursor_theme = strdup(cursor->xcursor_manager->name);
cursor->seat->state.cursor_size = cursor->xcursor_manager->size;
wl_signal_emit_mutable(&cursor->seat->events.cursor_configure, NULL);
}
struct cursor *cursor_create(struct seat *seat)
{
struct cursor *cursor = calloc(1, sizeof(*cursor));
if (!cursor) {
return NULL;
}
struct wlr_cursor *wlr_cursor = wlr_cursor_create();
if (!wlr_cursor) {
free(cursor);
return NULL;
}
cursor->seat = seat;
seat->cursor = cursor;
cursor->wlr_cursor = wlr_cursor;
cursor->pointer_touch_id = -1;
// TODO: multi-layout for multi-seat
wlr_cursor_attach_output_layout(wlr_cursor, seat->layout);
const char *xcursor_theme = getenv("XCURSOR_THEME");
const char *xcursor_size = getenv("XCURSOR_SIZE");
/* xcursor manager per seat for cursor theme */
cursor_set_xcursor_manager(
cursor, xcursor_theme ? xcursor_theme : seat->state.cursor_theme,
xcursor_size ? (uint32_t)atoi(xcursor_size) : seat->state.cursor_size, false);
cursor->motion.notify = cursor_handle_motion;
wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
cursor->motion_absolute.notify = cursor_handle_motion_absolute;
wl_signal_add(&wlr_cursor->events.motion_absolute, &cursor->motion_absolute);
cursor->button.notify = cursor_handle_button;
wl_signal_add(&wlr_cursor->events.button, &cursor->button);
cursor->axis.notify = cursor_handle_axis;
wl_signal_add(&wlr_cursor->events.axis, &cursor->axis);
cursor->frame.notify = cursor_handle_frame;
wl_signal_add(&wlr_cursor->events.frame, &cursor->frame);
cursor->swipe_begin.notify = cursor_handle_swipe_begin;
wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);
cursor->swipe_update.notify = cursor_handle_swipe_update;
wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
cursor->swipe_end.notify = cursor_handle_swipe_end;
wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
cursor->pinch_begin.notify = cursor_handle_pinch_begin;
wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);
cursor->pinch_update.notify = cursor_handle_pinch_update;
wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);
cursor->pinch_end.notify = cursor_handle_pinch_end;
wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);
cursor->hold_begin.notify = cursor_handle_hold_begin;
wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
cursor->hold_end.notify = cursor_handle_hold_end;
wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
cursor->touch_up.notify = cursor_handle_touch_up;
wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up);
cursor->touch_down.notify = cursor_handle_touch_down;
wl_signal_add(&wlr_cursor->events.touch_down, &cursor->touch_down);
cursor->touch_motion.notify = cursor_handle_touch_motion;
wl_signal_add(&wlr_cursor->events.touch_motion, &cursor->touch_motion);
cursor->touch_cancel.notify = cursor_handle_touch_cancel;
wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel);
cursor->touch_frame.notify = cursor_handle_touch_frame;
wl_signal_add(&wlr_cursor->events.touch_frame, &cursor->touch_frame);
cursor->tablet_tool_axis.notify = cursor_handle_tablet_tool_axis;
wl_signal_add(&wlr_cursor->events.tablet_tool_axis, &cursor->tablet_tool_axis);
cursor->tablet_tool_proximity.notify = cursor_handle_tablet_tool_proximity;
wl_signal_add(&wlr_cursor->events.tablet_tool_proximity, &cursor->tablet_tool_proximity);
cursor->tablet_tool_tip.notify = cursor_handle_tablet_tool_tip;
wl_signal_add(&wlr_cursor->events.tablet_tool_tip, &cursor->tablet_tool_tip);
cursor->tablet_tool_button.notify = cursor_handle_tablet_tool_button;
wl_signal_add(&wlr_cursor->events.tablet_tool_button, &cursor->tablet_tool_button);
cursor->request_set_cursor.notify = cursor_handle_request_set_cursor;
wl_signal_add(&seat->wlr_seat->events.request_set_cursor, &cursor->request_set_cursor);
cursor->surface_client_commit.notify = cursor_handle_surface_client_commit;
wl_list_init(&cursor->surface_client_commit.link);
cursor->surface_destroy.notify = cursor_handle_surface_destroy;
wl_list_init(&cursor->surface_destroy.link);
cursor->hover.destroy.notify = cursor_node_handle_destroy;
cursor->focus.destroy.notify = cursor_node_handle_destroy;
gesture_state_init(&cursor->gestures, seat->wlr_seat->display);
return cursor;
}
void cursor_destroy(struct cursor *cursor)
{
wl_list_remove(&cursor->motion.link);
wl_list_remove(&cursor->motion_absolute.link);
wl_list_remove(&cursor->button.link);
wl_list_remove(&cursor->axis.link);
wl_list_remove(&cursor->frame.link);
wl_list_remove(&cursor->request_set_cursor.link);
wl_list_remove(&cursor->swipe_begin.link);
wl_list_remove(&cursor->swipe_update.link);
wl_list_remove(&cursor->swipe_end.link);
wl_list_remove(&cursor->pinch_begin.link);
wl_list_remove(&cursor->pinch_update.link);
wl_list_remove(&cursor->pinch_end.link);
wl_list_remove(&cursor->hold_begin.link);
wl_list_remove(&cursor->hold_end.link);
wl_list_remove(&cursor->touch_up.link);
wl_list_remove(&cursor->touch_down.link);
wl_list_remove(&cursor->touch_motion.link);
wl_list_remove(&cursor->touch_cancel.link);
wl_list_remove(&cursor->touch_frame.link);
wl_list_remove(&cursor->tablet_tool_axis.link);
wl_list_remove(&cursor->tablet_tool_proximity.link);
wl_list_remove(&cursor->tablet_tool_tip.link);
wl_list_remove(&cursor->tablet_tool_button.link);
wl_list_remove(&cursor->surface_client_commit.link);
wl_list_remove(&cursor->surface_destroy.link);
if (cursor->hover.node) {
wl_list_remove(&cursor->hover.destroy.link);
}
if (cursor->focus.node) {
wl_list_remove(&cursor->focus.destroy.link);
}
wlr_xcursor_manager_destroy(cursor->xcursor_manager);
wlr_cursor_destroy(cursor->wlr_cursor);
gesture_state_finish(&cursor->gestures);
cursor->seat->cursor = NULL;
free(cursor);
}
void curosr_add_input(struct seat *seat, struct input *input)
{
struct wlr_cursor *wlr_cursor = seat->cursor->wlr_cursor;
wlr_cursor_attach_input_device(wlr_cursor, input->wlr_input);
}
void cursor_remove_input(struct input *input)
{
struct wlr_cursor *wlr_cursor = input->seat->cursor->wlr_cursor;
wlr_cursor_detach_input_device(wlr_cursor, input->wlr_input);
}
static void _cursor_set_image(struct cursor *cursor, enum cursor_name name, bool force)
{
struct server *server = cursor->seat->manager->server;
if (server->terminate) {
return;
}
if (cursor->image_locks > 0 || cursor->hidden) {
return;
}
/* early return if cursor not changed when client not requested */
if (!force && name == cursor->name && !cursor->client_requested) {
return;
}
if (name == CURSOR_NONE) {
wlr_cursor_unset_image(cursor->wlr_cursor);
} else {
wlr_cursor_set_xcursor(cursor->wlr_cursor, cursor->xcursor_manager, cursor_image[name]);
}
cursor->client_requested = false;
cursor->name = name;
kywc_log(KYWC_DEBUG, "Set %s cursor to %s", cursor->seat->name, cursor_image[name]);
}
void cursor_set_image(struct cursor *cursor, enum cursor_name name)
{
_cursor_set_image(cursor, name, false);
}
void cursor_set_resize_image(struct cursor *cursor, uint32_t edges)
{
enum cursor_name name = CURSOR_DEFAULT;
if (edges == (KYWC_EDGE_TOP | KYWC_EDGE_LEFT)) {
name = CURSOR_RESIZE_TOP_LEFT;
} else if (edges == KYWC_EDGE_TOP) {
name = CURSOR_RESIZE_TOP;
} else if (edges == (KYWC_EDGE_TOP | KYWC_EDGE_RIGHT)) {
name = CURSOR_RESIZE_TOP_RIGHT;
} else if (edges == KYWC_EDGE_RIGHT) {
name = CURSOR_RESIZE_RIGHT;
} else if (edges == (KYWC_EDGE_BOTTOM | KYWC_EDGE_RIGHT)) {
name = CURSOR_RESIZE_BOTTOM_RIGHT;
} else if (edges == KYWC_EDGE_BOTTOM) {
name = CURSOR_RESIZE_BOTTOM;
} else if (edges == (KYWC_EDGE_BOTTOM | KYWC_EDGE_LEFT)) {
name = CURSOR_RESIZE_BOTTOM_LEFT;
} else if (edges == KYWC_EDGE_LEFT) {
name = CURSOR_RESIZE_LEFT;
}
_cursor_set_image(cursor, name, false);
}
void cursor_rebase(struct cursor *cursor)
{
cursor_move(cursor, NULL, 0, 0, true, false);
_cursor_set_image(cursor, CURSOR_DEFAULT, true);
cursor_feed_fake_motion(cursor, true);
}
static bool seat_rebase_cursor(struct seat *seat, int index, void *data)
{
struct cursor *cursor = seat->cursor;
bool need_rebase = *(bool *)data;
if (!need_rebase) {
struct ky_scene_node *node =
ky_scene_node_at(&seat->scene->tree.node, cursor->lx, cursor->ly, NULL, NULL);
need_rebase = node != cursor->hover.node;
}
if (need_rebase) {
cursor_rebase(cursor);
}
return false;
}
void cursor_rebase_all(bool force)
{
input_manager_for_each_seat(seat_rebase_cursor, &force);
}
void cursor_move(struct cursor *cursor, struct wlr_input_device *dev, double x, double y,
bool delta, bool absolute)
{
struct wlr_cursor *wlr_cursor = cursor->wlr_cursor;
if (delta) {
wlr_cursor_move(wlr_cursor, dev, x, y);
} else if (absolute) {
wlr_cursor_warp_absolute(wlr_cursor, dev, x, y);
} else {
wlr_cursor_warp(wlr_cursor, dev, x, y);
}
cursor->lx = wlr_cursor->x;
cursor->ly = wlr_cursor->y;
struct seat_cursor_motion_event event = {
.device = dev ? input_from_wlr_input(dev) : NULL,
.time_msec = current_time_msec(),
.lx = cursor->lx,
.ly = cursor->ly,
};
wl_signal_emit_mutable(&cursor->seat->events.cursor_motion, &event);
}
void cursor_set_hidden(struct cursor *cursor, bool hidden)
{
if (cursor->hidden == hidden) {
return;
}
if (hidden) {
cursor_set_image(cursor, CURSOR_NONE);
cursor->hidden = true;
} else {
cursor->hidden = false;
cursor_rebase(cursor);
}
}
void cursor_lock_image(struct cursor *cursor, bool lock)
{
if (lock) {
++cursor->image_locks;
} else {
assert(cursor->image_locks > 0);
--cursor->image_locks;
}
}
static void cursor_constraint_warp_to_hint(struct cursor_constraint *constraint)
{
struct wlr_pointer_constraint_v1_state *current = &constraint->constraint->current;
if (!current->cursor_hint.enabled) {
return;
}
struct wlr_surface *surface = constraint->constraint->surface;
struct ky_scene_buffer *buffer = ky_scene_buffer_try_from_surface(surface);
if (!buffer) {
return;
}
int lx, ly;
ky_scene_node_coords(&buffer->node, &lx, &ly);
double sx = current->cursor_hint.x;
double sy = current->cursor_hint.y;
cursor_move(constraint->cursor, NULL, lx + sx, ly + sy, false, false);
wlr_seat_pointer_warp(constraint->cursor->seat->wlr_seat, sx, sy);
}
static void cursor_constraint_deactivate(void *data)
{
struct cursor_constraint *constraint = data;
wlr_pointer_constraint_v1_send_deactivated(constraint->constraint);
}
static void cursor_active_constraint(struct cursor *cursor, struct cursor_constraint *constraint,
bool deactivate_later)
{
struct cursor_constraint *old_constraint = cursor->active_constraint;
if (old_constraint == constraint) {
return;
}
cursor->active_constraint = constraint;
if (old_constraint) {
wl_list_remove(&old_constraint->surface_unmap.link);
wl_list_init(&old_constraint->surface_unmap.link);
wl_list_remove(&old_constraint->set_region.link);
wl_list_init(&old_constraint->set_region.link);
if (!constraint) {
cursor_constraint_warp_to_hint(old_constraint);
}
if (deactivate_later) {
struct wl_event_loop *loop = wl_display_get_event_loop(cursor->seat->wlr_seat->display);
wl_event_loop_add_idle(loop, cursor_constraint_deactivate, old_constraint);
} else {
wlr_pointer_constraint_v1_send_deactivated(old_constraint->constraint);
}
}
if (!constraint) {
return;
}
cursor->pending_constraint = NULL;
wlr_pointer_constraint_v1_send_activated(constraint->constraint);
struct wlr_surface *surface = constraint->constraint->surface;
wl_signal_add(&surface->events.unmap, &constraint->surface_unmap);
}
static bool cursor_constraint_check_region(struct cursor_constraint *constraint)
{
struct wlr_surface *surface = constraint->constraint->surface;
struct ky_scene_buffer *buffer = ky_scene_buffer_try_from_surface(surface);
if (!buffer) {
return false;
}
int lx, ly;
ky_scene_node_coords(&buffer->node, &lx, &ly);
int sx = constraint->cursor->lx - lx;
int sy = constraint->cursor->ly - ly;
return pixman_region32_contains_point(&constraint->constraint->region, sx, sy, NULL);
}
static void cursor_set_constraint(struct cursor *cursor, struct cursor_constraint *constraint);
static void cursor_constraint_handle_set_region(struct wl_listener *listener, void *data)
{
struct cursor_constraint *constraint = wl_container_of(listener, constraint, set_region);
struct wlr_pointer_constraint_v1 *wlr_constraint = constraint->constraint;
struct cursor *cursor = constraint->cursor;
/* check if the cursor is in the region */
bool in_region = cursor_constraint_check_region(constraint);
/* type always be WLR_POINTER_CONSTRAINT_V1_CONFINED */
if (cursor->pending_constraint == constraint && in_region) {
cursor_active_constraint(cursor, cursor->pending_constraint, false);
} else if (cursor->active_constraint == constraint && !in_region) {
/* deactivate the constraint and set pending if not oneshot */
bool oneshot = wlr_constraint->lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT;
cursor_active_constraint(cursor, NULL, oneshot);
if (!oneshot) {
cursor_set_constraint(cursor, constraint);
}
}
}
static void cursor_set_constraint(struct cursor *cursor, struct cursor_constraint *constraint)
{
if (cursor->active_constraint) {
if (cursor->active_constraint == constraint) {
return;
}
/* clear activated constraint always */
cursor_active_constraint(cursor, NULL, false);
}
if (cursor->pending_constraint == constraint) {
return;
}
if (cursor->pending_constraint) {
wl_list_remove(&cursor->pending_constraint->set_region.link);
wl_list_init(&cursor->pending_constraint->set_region.link);
}
cursor->pending_constraint = constraint;
if (!constraint) {
return;
}
if (constraint->constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) {
wl_signal_add(&constraint->constraint->events.set_region, &constraint->set_region);
}
/* activate this constraint if cursor is in the region */
if (cursor_constraint_check_region(constraint)) {
cursor_active_constraint(cursor, constraint, false);
}
}
static bool cursor_apply_constraint(struct cursor *cursor, struct wlr_input_device *device,
double *dx, double *dy)
{
struct cursor_constraint *constraint = cursor->active_constraint;
if (!constraint) {
constraint = cursor->pending_constraint;
}
bool has_constraint = constraint && (!device || device->type == WLR_INPUT_DEVICE_POINTER);
if (!has_constraint) {
return false;
}
/* no need to check the region once locked */
if (cursor->active_constraint &&
cursor->active_constraint->constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
return true;
}
struct wlr_surface *surface = constraint->constraint->surface;
struct ky_scene_buffer *buffer = ky_scene_buffer_try_from_surface(surface);
if (!buffer) {
return false;
}
int lx, ly;
ky_scene_node_coords(&buffer->node, &lx, &ly);
double sx = constraint->cursor->lx - lx, sy = constraint->cursor->ly - ly;
double sx_confined, sy_confined;
if (!wlr_region_confine(&constraint->constraint->region, sx, sy, sx + *dx, sy + *dy,
&sx_confined, &sy_confined)) {
return false;
}
/* activate the pending constraint */
if (constraint == cursor->pending_constraint) {
cursor_active_constraint(cursor, constraint, false);
}
if (!cursor->active_constraint) {
return false;
}
if (cursor->active_constraint->constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
return true;
}
*dx = sx_confined - sx;
*dy = sy_confined - sy;
return false;
}
static void cursor_constraint_handle_surface_unmap(struct wl_listener *listener, void *data)
{
struct cursor_constraint *constraint = wl_container_of(listener, constraint, surface_unmap);
cursor_active_constraint(constraint->cursor, NULL, false);
}
static void cursor_constraint_handle_destroy(struct wl_listener *listener, void *data)
{
struct cursor_constraint *constraint = wl_container_of(listener, constraint, destroy);
struct cursor *cursor = constraint->cursor;
wl_list_remove(&constraint->destroy.link);
wl_list_remove(&constraint->set_region.link);
wl_list_remove(&constraint->surface_unmap.link);
if (cursor->active_constraint == constraint) {
cursor_constraint_warp_to_hint(constraint);
cursor->active_constraint = NULL;
} else if (cursor->pending_constraint == constraint) {
cursor->pending_constraint = NULL;
}
free(constraint);
}
struct cursor_constraint *cursor_constraint_create(struct cursor *cursor,
struct wlr_pointer_constraint_v1 *constraint)
{
struct cursor_constraint *cursor_constraint = calloc(1, sizeof(*cursor_constraint));
if (!cursor_constraint) {
return NULL;
}
cursor_constraint->cursor = cursor;
cursor_constraint->constraint = constraint;
constraint->data = cursor_constraint;
cursor_constraint->set_region.notify = cursor_constraint_handle_set_region;
wl_list_init(&cursor_constraint->set_region.link);
cursor_constraint->destroy.notify = cursor_constraint_handle_destroy;
wl_signal_add(&constraint->events.destroy, &cursor_constraint->destroy);
cursor_constraint->surface_unmap.notify = cursor_constraint_handle_surface_unmap;
wl_list_init(&cursor_constraint->surface_unmap.link);
struct wlr_surface *surface = cursor->seat->wlr_seat->keyboard_state.focused_surface;
if (surface && surface == constraint->surface) {
cursor_set_constraint(cursor, cursor_constraint);
}
return cursor_constraint;
}
void cursor_constraint_set_focus(struct seat *seat, struct wlr_surface *surface)
{
struct wlr_pointer_constraint_v1 *constraint =
wlr_pointer_constraints_v1_constraint_for_surface(seat->manager->pointer_constraints,
surface, seat->wlr_seat);
struct cursor_constraint *cursor_constraint = constraint ? constraint->data : NULL;
cursor_set_constraint(seat->cursor, cursor_constraint);
}
kylin-wayland-compositor/src/meson.build 0000664 0001750 0001750 00000000547 15160460057 017422 0 ustar feng feng wlcom_sources += files(
'main.c',
'server.c',
'plugin/config.c',
'plugin/plugin.c',
)
subdir('backend')
subdir('config')
subdir('effect')
subdir('input')
subdir('output')
subdir('painter')
subdir('render')
subdir('scene')
subdir('security')
subdir('theme')
subdir('util')
subdir('view')
subdir('widget')
if have_xwayland
subdir('xwayland')
endif
kylin-wayland-compositor/src/view/ 0000775 0001750 0001750 00000000000 15160461067 016226 5 ustar feng feng kylin-wayland-compositor/src/view/kde_blur.c 0000664 0001750 0001750 00000020563 15160461067 020167 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include "blur-protocol.h"
#include "scene/surface.h"
#include "view_p.h"
#define KDE_KWIN_BLUR_MANAGER_VERSION 1
enum KDE_BLUR_STATE_MASK {
KDE_BLUR_STATE_NONE = 0,
KDE_BLUR_STATE_REGION = 1 << 0,
};
struct kde_blur_manager {
struct wl_global *global;
struct wl_list kde_blurs;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct kde_blur {
struct wl_list link;
struct wl_list resources;
struct wlr_surface *wlr_surface;
struct wl_listener surface_map;
struct wl_listener surface_destroy;
struct ky_scene_buffer *scene_buffer;
struct wl_listener node_destroy;
pixman_region32_t region, pending_region;
uint32_t pending_mask;
};
static struct kde_blur_manager *manager = NULL;
static void kde_blur_apply_state(struct kde_blur *blur)
{
if (blur->pending_mask & KDE_BLUR_STATE_REGION) {
ky_scene_node_set_blur_region(&blur->scene_buffer->node, &blur->region);
}
blur->pending_mask = KDE_BLUR_STATE_NONE;
}
static struct kde_blur *kde_blur_from_wlr_surface(struct wlr_surface *wlr_surface)
{
struct kde_blur *blur;
wl_list_for_each(blur, &manager->kde_blurs, link) {
if (blur->wlr_surface == wlr_surface) {
return blur;
}
}
return NULL;
}
static void kde_blur_handle_commit(struct wl_client *client, struct wl_resource *resource)
{
struct kde_blur *blur = wl_resource_get_user_data(resource);
if (!blur) {
return;
}
pixman_region32_copy(&blur->region, &blur->pending_region);
if (!blur->wlr_surface->mapped) {
return;
}
kde_blur_apply_state(blur);
}
static void kde_blur_handle_set_region(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *region_resource)
{
struct kde_blur *blur = wl_resource_get_user_data(resource);
if (!blur) {
return;
}
if (region_resource) {
const pixman_region32_t *region = wlr_region_from_resource(region_resource);
pixman_region32_copy(&blur->pending_region, region);
} else {
pixman_region32_clear(&blur->pending_region);
}
blur->pending_mask |= KDE_BLUR_STATE_REGION;
}
static void kde_blur_handle_release(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct org_kde_kwin_blur_interface kde_blur_impl = {
.commit = kde_blur_handle_commit,
.set_region = kde_blur_handle_set_region,
.release = kde_blur_handle_release,
};
static void kde_blur_destroy(struct kde_blur *blur)
{
/* remove blur if surface is not destroyed */
if (blur->scene_buffer) {
ky_scene_node_set_blur_region(&blur->scene_buffer->node, NULL);
}
/* clear destructor when surface destroyed before blur resources */
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &blur->resources) {
wl_resource_set_user_data(resource, NULL);
wl_resource_set_destructor(resource, NULL);
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
}
wl_list_remove(&blur->link);
wl_list_remove(&blur->surface_map.link);
wl_list_remove(&blur->surface_destroy.link);
wl_list_remove(&blur->node_destroy.link);
pixman_region32_fini(&blur->region);
pixman_region32_fini(&blur->pending_region);
free(blur);
}
static void kde_blur_handle_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
/* destroy blur if no blur resource */
struct kde_blur *blur = wl_resource_get_user_data(resource);
if (blur && wl_list_empty(&blur->resources)) {
kde_blur_destroy(blur);
}
}
static void blur_handle_node_destroy(struct wl_listener *listener, void *data)
{
struct kde_blur *blur = wl_container_of(listener, blur, node_destroy);
blur->scene_buffer = NULL;
kde_blur_destroy(blur);
}
static void blur_handle_surface_map(struct wl_listener *listener, void *data)
{
struct kde_blur *blur = wl_container_of(listener, blur, surface_map);
wl_list_remove(&blur->surface_map.link);
wl_list_init(&blur->surface_map.link);
blur->scene_buffer = ky_scene_buffer_try_from_surface(blur->wlr_surface);
wl_signal_add(&blur->scene_buffer->node.events.destroy, &blur->node_destroy);
kde_blur_apply_state(blur);
}
static void blur_handle_surface_destroy(struct wl_listener *listener, void *data)
{
struct kde_blur *blur = wl_container_of(listener, blur, surface_destroy);
kde_blur_destroy(blur);
}
static void handle_create(struct wl_client *client, struct wl_resource *manager_resource,
uint32_t id, struct wl_resource *surface_resource)
{
struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource);
struct kde_blur *blur = kde_blur_from_wlr_surface(wlr_surface);
if (!blur) {
blur = calloc(1, sizeof(*blur));
if (!blur) {
wl_client_post_no_memory(client);
return;
}
wl_list_init(&blur->resources);
wl_list_insert(&manager->kde_blurs, &blur->link);
pixman_region32_init(&blur->region);
pixman_region32_init(&blur->pending_region);
blur->wlr_surface = wlr_surface;
blur->surface_map.notify = blur_handle_surface_map;
wl_signal_add(&wlr_surface->events.map, &blur->surface_map);
blur->surface_destroy.notify = blur_handle_surface_destroy;
wl_signal_add(&wlr_surface->events.destroy, &blur->surface_destroy);
blur->node_destroy.notify = blur_handle_node_destroy;
wl_list_init(&blur->node_destroy.link);
if (wlr_surface->mapped) {
blur_handle_surface_map(&blur->surface_map, NULL);
}
}
int version = wl_resource_get_version(manager_resource);
struct wl_resource *resource =
wl_resource_create(client, &org_kde_kwin_blur_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_list_insert(&blur->resources, wl_resource_get_link(resource));
wl_resource_set_implementation(resource, &kde_blur_impl, blur,
kde_blur_handle_resource_destroy);
}
static void handle_unset(struct wl_client *client, struct wl_resource *manager_resource,
struct wl_resource *surface_resource)
{
struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource);
struct kde_blur *blur = kde_blur_from_wlr_surface(wlr_surface);
if (blur) {
kde_blur_destroy(blur);
}
}
static const struct org_kde_kwin_blur_manager_interface kde_blur_manager_impl = {
.create = handle_create,
.unset = handle_unset,
};
static void kde_blur_manager_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &org_kde_kwin_blur_manager_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &kde_blur_manager_impl, manager, NULL);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->server_destroy.link);
free(manager);
manager = NULL;
}
bool kde_blur_manager_create(struct server *server)
{
manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->global =
wl_global_create(server->display, &org_kde_kwin_blur_manager_interface,
KDE_KWIN_BLUR_MANAGER_VERSION, manager, kde_blur_manager_bind);
if (!manager->global) {
kywc_log(KYWC_WARN, "Kde blur manager create failed");
free(manager);
return false;
}
wl_list_init(&manager->kde_blurs);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &manager->server_destroy);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &manager->display_destroy);
return true;
}
kylin-wayland-compositor/src/view/stack_mode.c 0000664 0001750 0001750 00000017511 15160461067 020510 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include "view_p.h"
struct stack_mode_view {
struct wl_list link;
char *uuid;
struct {
bool maximized, fullscreen;
enum kywc_tile tiled;
struct kywc_box geometry;
} saved;
};
static struct stack_mode_manager {
struct wl_list stack_views;
struct view_manager *view_manager;
} *manager = NULL;
static struct stack_mode_view *stack_mode_view_from_uuid(const char *uuid)
{
if (!uuid) {
return NULL;
}
struct stack_mode_view *stack_view;
wl_list_for_each(stack_view, &manager->stack_views, link) {
if (strcmp(stack_view->uuid, uuid) == 0) {
return stack_view;
}
}
return NULL;
}
static void stack_mode_view_destroy(struct stack_mode_view *stack_view)
{
wl_list_remove(&stack_view->link);
free(stack_view->uuid);
free(stack_view);
}
static void stack_mode_view_save(struct view *view)
{
struct stack_mode_view *stack_view = calloc(1, sizeof(*stack_view));
if (!stack_view) {
return;
}
wl_list_insert(&manager->stack_views, &stack_view->link);
stack_view->uuid = strdup(view->base.uuid);
stack_view->saved.maximized = view->base.maximized;
stack_view->saved.fullscreen = view->base.fullscreen;
stack_view->saved.tiled = view->base.tiled;
stack_view->saved.geometry = view->base.geometry;
}
static void stack_mode_view_restore(struct view *view)
{
struct stack_mode_view *stack_view = stack_mode_view_from_uuid(view->base.uuid);
if (!stack_view) {
return;
}
if (stack_view->saved.fullscreen) {
view_do_fullscreen(view, true, view->output);
} else if (stack_view->saved.maximized) {
if (view->base.fullscreen) {
view_do_fullscreen(view, false, view->output);
}
view_do_maximized(view, true, view->output);
} else if (stack_view->saved.tiled) {
if (view->base.fullscreen) {
view_do_fullscreen(view, false, view->output);
}
if (view->base.maximized) {
view_do_maximized(view, false, view->output);
}
if (manager->view_manager->impl.set_tiled) {
manager->view_manager->impl.set_tiled(view, stack_view->saved.tiled, view->output);
}
} else {
if (view->base.fullscreen) {
view_do_fullscreen(view, false, view->output);
}
if (view->base.maximized) {
view_do_maximized(view, false, view->output);
}
view_do_resize(view, &stack_view->saved.geometry);
}
stack_mode_view_destroy(stack_view);
}
static void stack_mode_view_map(struct view *view)
{
/* init view position */
positioner_add_new_view(view);
}
static void stack_mode_view_move(struct view *view, int x, int y)
{
if (!KYWC_VIEW_IS_MOVABLE(&view->base)) {
return;
}
view_do_move(view, x, y);
}
static void stack_mode_view_resize(struct view *view, struct kywc_box *geometry)
{
if (!KYWC_VIEW_IS_RESIZABLE(&view->base)) {
return;
}
view_do_resize(view, geometry);
}
static void stack_mode_view_minimized(struct view *view, bool minimized)
{
if (!KYWC_VIEW_IS_MINIMIZABLE(&view->base)) {
return;
}
view_do_minimized(view, minimized);
}
static void stack_mode_view_maximized(struct view *view, bool maximized,
struct kywc_output *kywc_output)
{
if (!KYWC_VIEW_IS_MAXIMIZABLE(&view->base)) {
return;
}
view_do_maximized(view, maximized, kywc_output);
}
static void stack_mode_view_fullscreen(struct view *view, bool fullscreen,
struct kywc_output *kywc_output)
{
if (!KYWC_VIEW_IS_FULLSCREENABLE(&view->base)) {
return;
}
view_do_fullscreen(view, fullscreen, kywc_output);
}
static void stack_mode_view_tiled(struct view *view, enum kywc_tile tile,
struct kywc_output *kywc_output)
{
if (!KYWC_VIEW_IS_RESIZABLE(&view->base)) {
return;
}
if (manager->view_manager->impl.set_tiled) {
manager->view_manager->impl.set_tiled(view, tile, kywc_output);
}
}
static void stack_mode_view_activate(struct view *view)
{
if (!KYWC_VIEW_IS_ACTIVATABLE(&view->base)) {
return;
}
struct view *descendant = view_find_descendant_modal(view);
view_do_activate(descendant ? descendant : view);
view_raise_to_top(view, true);
}
static void stack_mode_view_show_menu(struct view *view, struct seat *seat, int x, int y)
{
if (!view->current_proxy) {
return;
}
window_menu_show(view, seat, x, y);
}
static void stack_mode_view_show_tile_flyout(struct view *view, struct seat *seat,
struct kywc_box *box)
{
if (!view->current_proxy) {
return;
}
tile_flyout_show(view, seat, box);
}
static void stack_mode_view_show_linkage_bar(struct view *view, uint32_t edges)
{
if (!view->current_proxy) {
return;
}
tile_linkage_bar_show(view, edges);
}
static void stack_mode_view_click(struct seat *seat, struct view *view, uint32_t button,
bool pressed, enum click_state state)
{
struct kywc_view *kywc_view = &view->base;
/* active current view */
kywc_view_activate(kywc_view);
view_set_focus(view, seat);
}
static void stack_mode_enter(void)
{
struct view *view;
struct view_manager *view_manager = manager->view_manager;
wl_list_for_each(view, &view_manager->views, link) {
if (!view->base.mapped || view->base.minimized ||
view->base.role != KYWC_VIEW_ROLE_NORMAL) {
continue;
}
stack_mode_view_restore(view);
}
struct stack_mode_view *stack_view, *tmp;
wl_list_for_each_safe(stack_view, tmp, &manager->stack_views, link) {
stack_mode_view_destroy(stack_view);
}
}
static void stack_mode_leave(void)
{
struct view_manager *view_manager = manager->view_manager;
struct view *view;
wl_list_for_each(view, &view_manager->views, link) {
if (!view->base.mapped || view->base.role != KYWC_VIEW_ROLE_NORMAL) {
continue;
}
stack_mode_view_save(view);
}
}
static void stack_mode_destroy(void)
{
if (!manager) {
return;
}
struct stack_mode_view *stack_view, *tmp;
wl_list_for_each_safe(stack_view, tmp, &manager->stack_views, link) {
stack_mode_view_destroy(stack_view);
}
free(manager);
manager = NULL;
}
static const struct view_mode_interface stack_mode_impl = {
.name = "stack_mode",
.view_map = stack_mode_view_map,
.view_unmap = NULL,
.view_request_move = stack_mode_view_move,
.view_request_resize = stack_mode_view_resize,
.view_request_minimized = stack_mode_view_minimized,
.view_request_maximized = stack_mode_view_maximized,
.view_request_fullscreen = stack_mode_view_fullscreen,
.view_request_tiled = stack_mode_view_tiled,
.view_request_activate = stack_mode_view_activate,
.view_request_show_menu = stack_mode_view_show_menu,
.view_request_show_tile_flyout = stack_mode_view_show_tile_flyout,
.view_request_show_tile_linkage_bar = stack_mode_view_show_linkage_bar,
.view_click = stack_mode_view_click,
.view_hover = NULL,
.view_mode_enter = stack_mode_enter,
.view_mode_leave = stack_mode_leave,
.mode_destroy = stack_mode_destroy,
};
void stack_mode_register(struct view_manager *view_manager)
{
struct view_mode *mode = view_manager_mode_register(&stack_mode_impl);
if (!mode) {
return;
}
manager = calloc(1, sizeof(*manager));
if (!manager) {
view_manager_mode_unregister(mode);
return;
}
wl_list_init(&manager->stack_views);
manager->view_manager = view_manager;
}
kylin-wayland-compositor/src/view/interactive.c 0000664 0001750 0001750 00000126071 15160461067 020716 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include "effect/move.h"
#include "effect/node_transform.h"
#include "input/cursor.h"
#include "input/seat.h"
#include "output.h"
#include "scene/surface.h"
#include "theme.h"
#include "util/color.h"
#include "util/macros.h"
#include "util/time.h"
#include "view/action.h"
#include "view/workspace.h"
#include "view_p.h"
#define VIEW_EDGE_GAP 20
#define VIEW_TOP_GAP 5
#define VIEW_BOTTOM_GAP 100
#define VIEW_LEFT_GAP 100
#define VIEW_RIGHT_GAP 100
#define VIEW_MIN_WIDTH 200
#define VIEW_MIN_HEIGHT 100
#define VIEW_MOVE_STEP 10
#define VIEW_RESIZE_STEP 10
#define SNAP_BOX_FILTER 200
#define SNAP_BORDER_CORNER_RATIO 0.25
#define EDGE_OFFSET 10
enum interactive_mode {
INTERACTIVE_MODE_NONE = 0,
INTERACTIVE_MODE_MOVE,
INTERACTIVE_MODE_RESIZE,
INTERACTIVE_MODE_TILE,
INTERACTIVE_MODE_TILE_HALF_SCREEN,
};
enum tile_state {
TILE_NONE = 0,
TILE_TOP,
TILE_BOTTOM,
TILE_LEFT,
TILE_RIGHT,
TILE_TOP_LEFT,
TILE_BOTTOM_LEFT,
TILE_TOP_RIGHT,
TILE_BOTTOM_RIGHT,
TILE_MINIMIZE,
TILE_MAXIMIZE,
};
struct interactive_tile_state {
int key_up;
int key_down;
int key_left;
int key_right;
};
const struct interactive_tile_state tile_states[] = {
{ TILE_MAXIMIZE, TILE_MINIMIZE, TILE_LEFT, TILE_RIGHT }, // TILE_NONE
{ TILE_TOP, TILE_NONE, TILE_TOP_LEFT, TILE_TOP_RIGHT }, // TILE_TOP
{ TILE_NONE, TILE_MINIMIZE, TILE_BOTTOM_LEFT, TILE_BOTTOM_RIGHT }, // TILE_BOTTOM
{ TILE_TOP_LEFT, TILE_BOTTOM_LEFT, TILE_RIGHT, TILE_NONE }, // TILE_LEFT
{ TILE_TOP_RIGHT, TILE_BOTTOM_RIGHT, TILE_NONE, TILE_LEFT }, // TILE_RIGHT
{ TILE_MAXIMIZE, TILE_LEFT, TILE_TOP_RIGHT, TILE_TOP_RIGHT }, // TILE_TOP_LEFT
{ TILE_LEFT, TILE_MINIMIZE, TILE_BOTTOM_RIGHT, TILE_BOTTOM_RIGHT }, // TILE_BOTTOM_LEFT
{ TILE_MAXIMIZE, TILE_RIGHT, TILE_TOP_LEFT, TILE_TOP_LEFT }, // TILE_TOP_RIGHT
{ TILE_RIGHT, TILE_MINIMIZE, TILE_BOTTOM_LEFT, TILE_BOTTOM_LEFT }, // TILE_BOTTOM_RIGHT
{ TILE_NONE, TILE_MINIMIZE, TILE_MINIMIZE, TILE_MINIMIZE }, // TILE_MINIMIZE
{ TILE_TOP, TILE_NONE, TILE_LEFT, TILE_RIGHT }, // TILE_MAXIMIZE
};
struct interactive_grab {
struct seat_pointer_grab pointer_grab;
struct seat_keyboard_grab keyboard_grab;
struct seat_touch_grab touch_grab;
struct seat *seat;
// struct wl_listener *seat_destroy;
struct output *output;
/* view being moved or resized */
struct view *view;
struct wl_listener view_unmap;
/* move or resize */
enum interactive_mode mode;
/* moved or resized actually */
bool ongoing;
/* tile view resize linkage */
bool has_linkage;
/* cursor position before move or resize */
double cursor_x, cursor_y;
/* view position and size before move or resize */
struct kywc_box geo;
/* resize edges */
uint32_t resize_edges;
uint32_t last_time;
/* snap_box */
struct ky_scene_node *snap_node;
struct ky_scene_rect *snap_rect;
struct output *snap_output;
enum kywc_tile snap_mode;
struct wl_event_source *filter;
bool filter_enabled;
/* move effect */
struct move_proxy *proxy;
struct wl_listener proxy_destroy;
struct kywc_box saved_usable_area;
bool update_saved_geo;
};
static enum kywc_tile get_kywc_tile_mode(struct interactive_grab *grab)
{
double cur_x = grab->seat->cursor->lx;
double cur_y = grab->seat->cursor->ly;
enum kywc_tile mode = KYWC_TILE_NONE;
/* current output usable area */
struct kywc_box *usable = &grab->output->usable_area;
int32_t y1 = cur_y - usable->y;
/* left */
if (cur_x - usable->x < VIEW_EDGE_GAP) {
/* top left*/
if (y1 < usable->height * SNAP_BORDER_CORNER_RATIO) {
mode = KYWC_TILE_TOP_LEFT;
/* bottom left */
} else if (y1 > usable->height - usable->height * SNAP_BORDER_CORNER_RATIO) {
mode = KYWC_TILE_BOTTOM_LEFT;
/* left */
} else {
mode = KYWC_TILE_LEFT;
}
/* right */
} else if (usable->x + usable->width - cur_x < VIEW_EDGE_GAP) {
/* top right*/
if (y1 < usable->height * SNAP_BORDER_CORNER_RATIO) {
mode = KYWC_TILE_TOP_RIGHT;
/* bottom right */
} else if (y1 > usable->height - usable->height * SNAP_BORDER_CORNER_RATIO) {
mode = KYWC_TILE_BOTTOM_RIGHT;
/* right */
} else {
mode = KYWC_TILE_RIGHT;
}
/* all */
} else if (y1 < VIEW_TOP_GAP) {
if (KYWC_VIEW_IS_MAXIMIZABLE(&grab->view->base)) {
mode = KYWC_TILE_ALL;
}
}
return mode;
}
static void snap_box_update(struct interactive_grab *grab, enum kywc_tile mode)
{
if (grab->snap_mode == mode && grab->snap_output == grab->output) {
return;
}
struct view *view = grab->view;
bool need_source_box = grab->snap_mode == KYWC_TILE_NONE;
if (!grab->snap_rect) {
struct ky_scene_node *sibling = &view->tree->node;
float color[4];
struct theme *theme = theme_manager_get_theme();
color_float_pax(color, theme->active_bg_color, theme->opacity / 100.0);
grab->snap_rect = ky_scene_rect_create(sibling->parent, 0, 0, color);
grab->snap_node = &grab->snap_rect->node;
/* add blur */
pixman_region32_t region;
pixman_region32_init(®ion);
ky_scene_node_set_blur_region(grab->snap_node, theme->opacity != 100 ? ®ion : NULL);
pixman_region32_fini(®ion);
ky_scene_node_place_below(grab->snap_node, sibling);
need_source_box = true;
}
grab->snap_mode = mode;
grab->snap_output = grab->output;
ky_scene_node_set_enabled(grab->snap_node, mode != KYWC_TILE_NONE);
if (mode == KYWC_TILE_NONE) {
return;
}
struct kywc_box geo = { 0 };
view_get_tiled_geometry(view, &geo, &grab->output->base, mode);
geo.x -= view->base.margin.off_x;
geo.y -= view->base.margin.off_y;
geo.width += view->base.margin.off_width;
geo.height += view->base.margin.off_height;
if (need_source_box) {
struct wlr_box output_box = { geo.x, geo.y, geo.width, geo.height };
struct wlr_box view_box = { view->base.geometry.x - view->base.margin.off_x,
view->base.geometry.y - view->base.margin.off_y,
view->base.geometry.width + view->base.margin.off_x,
view->base.geometry.height + view->base.margin.off_y };
struct wlr_box rect_box;
wlr_box_intersection(&rect_box, &output_box, &view_box);
ky_scene_rect_set_size(grab->snap_rect, rect_box.width, rect_box.height);
ky_scene_node_set_position(grab->snap_node, rect_box.x, rect_box.y);
}
struct kywc_box start_geo = { grab->snap_rect->node.x, grab->snap_rect->node.y,
grab->snap_rect->width, grab->snap_rect->height };
struct transform_options options = {
.start_time = current_time_msec(),
.duration = 200,
.below_view = true,
.start = { .geometry = start_geo, .alpha = 1.0 },
.end = { .geometry = geo, .alpha = 1.0 },
.animations = { .geometry = animation_manager_get(ANIMATION_TYPE_EASE_OUT) },
};
node_add_transform_effect(grab->snap_node, &options);
ky_scene_node_set_position(grab->snap_node, geo.x, geo.y);
ky_scene_rect_set_size(grab->snap_rect, geo.width, geo.height);
}
static void snap_box_enable_filter(struct interactive_grab *grab, bool enabled)
{
if (grab->filter_enabled == enabled) {
return;
}
grab->filter_enabled = enabled;
wl_event_source_timer_update(grab->filter, enabled ? SNAP_BOX_FILTER : 0);
}
static int handle_snap_box(void *data)
{
struct interactive_grab *grab = data;
grab->output = input_current_output(grab->seat);
enum kywc_tile mode = get_kywc_tile_mode(grab);
grab->filter_enabled = false;
snap_box_update(grab, mode);
return 0;
}
static void interactive_move_show_snap_box(struct interactive_grab *grab, int cur_x, int cur_y)
{
struct view *view = grab->view;
if (!KYWC_VIEW_IS_RESIZABLE(&view->base)) {
return;
}
struct kywc_box *usable = &grab->output->usable_area;
/* left */
if (cur_x - usable->x < VIEW_EDGE_GAP) {
/* trigger timer to show snap box if not the leftmost output */
if (!output_at_layout_edge(grab->output, LAYOUT_EDGE_LEFT) &&
grab->snap_mode != KYWC_TILE_TOP_LEFT && grab->snap_mode != KYWC_TILE_LEFT &&
grab->snap_mode != KYWC_TILE_BOTTOM_LEFT) {
snap_box_update(grab, KYWC_TILE_NONE);
snap_box_enable_filter(grab, true);
return;
}
/* right */
} else if (usable->x + usable->width - cur_x < VIEW_EDGE_GAP) {
/* trigger timer to show snap box if not the rightmost output */
if (!output_at_layout_edge(grab->output, LAYOUT_EDGE_RIGHT) &&
grab->snap_mode != KYWC_TILE_RIGHT && grab->snap_mode != KYWC_TILE_TOP_RIGHT &&
grab->snap_mode != KYWC_TILE_BOTTOM_RIGHT) {
snap_box_update(grab, KYWC_TILE_NONE);
snap_box_enable_filter(grab, true);
return;
}
/* all */
} else if (cur_y - usable->y < VIEW_TOP_GAP) {
/* trigger timer to show snap box if not the topmost output */
if (KYWC_VIEW_IS_MAXIMIZABLE(&view->base) &&
!output_at_layout_edge(grab->output, LAYOUT_EDGE_TOP) &&
grab->snap_mode != KYWC_TILE_ALL) {
snap_box_update(grab, KYWC_TILE_NONE);
snap_box_enable_filter(grab, true);
return;
}
}
enum kywc_tile mode = get_kywc_tile_mode(grab);
snap_box_enable_filter(grab, false);
snap_box_update(grab, mode);
}
static void interactive_grab_destroy(struct interactive_grab *grab)
{
grab->view->interactive_moving = false;
grab->view->current_resize_edges = KYWC_EDGE_NONE;
wl_list_remove(&grab->view_unmap.link);
if (grab->proxy) {
wl_list_remove(&grab->proxy_destroy.link);
move_proxy_destroy(grab->proxy);
}
if (grab->has_linkage) {
tile_linkage_resize_done(grab->view, false);
grab->has_linkage = false;
}
cursor_set_image(grab->seat->cursor, CURSOR_DEFAULT);
seat_end_pointer_grab(grab->seat, &grab->pointer_grab);
seat_end_keyboard_grab(grab->seat, &grab->keyboard_grab);
seat_end_touch_grab(grab->seat, &grab->touch_grab);
ky_scene_node_destroy(grab->snap_node);
/* sync the position to client */
int lx = grab->view->base.geometry.x;
int ly = grab->view->base.geometry.y;
kywc_view_move(&grab->view->base, lx, ly);
wl_event_source_remove(grab->filter);
free(grab);
}
static void interactivate_done_move(struct interactive_grab *grab)
{
grab->output = input_current_output(grab->seat);
snap_box_update(grab, KYWC_TILE_NONE);
enum kywc_tile mode = get_kywc_tile_mode(grab);
if (mode == KYWC_TILE_NONE) {
return;
}
if (mode == KYWC_TILE_ALL) {
/* current cursor focused output, not the view most at output */
kywc_view_set_maximized(&grab->view->base, true, &grab->output->base);
} else {
kywc_view_set_tiled(&grab->view->base, mode, &grab->output->base);
}
if (grab->update_saved_geo) {
grab->view->saved.geometry = grab->geo;
if (!kywc_box_equal(&grab->saved_usable_area, &grab->output->usable_area)) {
view_fix_geometry(grab->view, &grab->view->saved.geometry, &grab->saved_usable_area,
&grab->output->usable_area);
}
}
}
static void interactive_done(struct interactive_grab *grab)
{
bool need_assist = false;
/* for snap to edge */
if (grab->view->base.mapped && grab->ongoing && grab->mode == INTERACTIVE_MODE_MOVE) {
interactivate_done_move(grab);
if (grab->view->base.tiled != KYWC_TILE_NONE && grab->view->base.tiled != KYWC_TILE_ALL) {
need_assist = true;
}
}
if (grab->mode == INTERACTIVE_MODE_TILE || grab->mode == INTERACTIVE_MODE_TILE_HALF_SCREEN) {
if (!grab->view->base.minimized && !grab->view->base.maximized && grab->view->base.tiled) {
need_assist = true;
}
}
struct view *view = grab->view;
struct output *output = grab->output;
struct seat *seat = grab->seat;
interactive_grab_destroy(grab);
if (need_assist) {
view_manager_show_tile_assist(view, seat, &output->base);
}
}
static void window_adsorption_top_or_bottom(struct kywc_box *s_box, const struct kywc_box *l_box,
int *offset, enum interactive_mode mode)
{
int sx1 = s_box->x, sy1 = s_box->y, sx2 = s_box->x + s_box->width,
sy2 = s_box->y + s_box->height;
int lx1 = l_box->x, ly1 = l_box->y, lx2 = l_box->x + l_box->width,
ly2 = l_box->y + l_box->height;
if (sx1 > lx2 || sx2 < lx1) {
return;
}
/* top adsorb bottom */
int temp = abs(ly2 - sy1);
if (temp < *offset) {
*offset = temp;
s_box->y = ly2;
if (mode == INTERACTIVE_MODE_RESIZE) {
s_box->height = sy2 - ly2;
}
return;
}
/* bottom adsorb top */
temp = abs(ly1 - sy2);
if (temp < *offset) {
*offset = temp;
if (mode == INTERACTIVE_MODE_MOVE) {
s_box->y = ly1 - (sy2 - sy1);
} else if (mode == INTERACTIVE_MODE_RESIZE) {
s_box->height += temp;
}
}
}
static void window_adsorption_left_or_right(struct kywc_box *s_box, const struct kywc_box *l_box,
int *offset, enum interactive_mode mode)
{
int sx1 = s_box->x, sy1 = s_box->y, sx2 = s_box->x + s_box->width,
sy2 = s_box->y + s_box->height;
int lx1 = l_box->x, ly1 = l_box->y, lx2 = l_box->x + l_box->width,
ly2 = l_box->y + l_box->height;
if (sy1 > ly2 || sy2 < ly1) {
return;
}
/* left adsorb right */
int temp = abs(lx2 - sx1);
if (temp < *offset) {
*offset = temp;
s_box->x = lx2;
if (mode == INTERACTIVE_MODE_RESIZE) {
s_box->width = sx2 - lx2;
}
return;
}
/* right adsorb left */
temp = abs(lx1 - sx2);
if (temp < *offset) {
*offset = temp;
if (mode == INTERACTIVE_MODE_MOVE) {
s_box->x = lx1 - (sx2 - sx1);
} else if (mode == INTERACTIVE_MODE_RESIZE) {
s_box->width += temp;
}
}
}
static void window_adsorb_window_constraints(struct kywc_view *kywc_view, struct kywc_box *pending,
int *gap_x, int *gap_y, uint32_t edges,
enum interactive_mode mode)
{
if ((view_manager_get_adsorption() & VIEW_ADSORPTION_WINDOW_EDGES) == 0) {
return;
}
/* actual window box */
struct kywc_box s_box = {
.x = pending->x - kywc_view->margin.off_x,
.y = pending->y - kywc_view->margin.off_y,
.width = pending->width + kywc_view->margin.off_width,
.height = pending->height + kywc_view->margin.off_height,
};
struct view_proxy *view_proxy;
struct workspace *workspace = workspace_manager_get_current();
wl_list_for_each(view_proxy, &workspace->view_proxies, workspace_link) {
if (!view_proxy->view->base.mapped || view_proxy->view == view_from_kywc_view(kywc_view) ||
view_proxy->view->base.minimized || view_proxy->view->base.maximized ||
view_proxy->view->base.fullscreen || !view_proxy->view->tree->node.enabled) {
continue;
}
/* be adsorbed window box */
struct kywc_box l_box = {
.x = view_proxy->view->base.geometry.x - view_proxy->view->base.margin.off_x,
.y = view_proxy->view->base.geometry.y - view_proxy->view->base.margin.off_y,
.width =
view_proxy->view->base.geometry.width + view_proxy->view->base.margin.off_width,
.height =
view_proxy->view->base.geometry.height + view_proxy->view->base.margin.off_height,
};
if (edges & KYWC_EDGE_LEFT || edges & KYWC_EDGE_RIGHT) {
window_adsorption_left_or_right(&s_box, &l_box, gap_x, mode);
}
if (edges & KYWC_EDGE_TOP || edges & KYWC_EDGE_BOTTOM) {
window_adsorption_top_or_bottom(&s_box, &l_box, gap_y, mode);
}
}
pending->x = s_box.x + kywc_view->margin.off_x;
pending->y = s_box.y + kywc_view->margin.off_y;
if (mode == INTERACTIVE_MODE_RESIZE) {
pending->width = s_box.width - kywc_view->margin.off_width;
pending->height = s_box.height - kywc_view->margin.off_height;
}
}
static void window_adsorb_edges_constraints(struct kywc_view *kywc_view, struct kywc_box *pending,
struct output *output, int *gap_x, int *gap_y)
{
if ((view_manager_get_adsorption() & VIEW_ADSORPTION_SCREEN_EDGES) == 0) {
return;
}
/* actual window box */
struct kywc_box s_box = {
.x = pending->x - kywc_view->margin.off_x,
.y = pending->y - kywc_view->margin.off_y,
.width = pending->width + kywc_view->margin.off_width,
.height = pending->height + kywc_view->margin.off_height,
};
/* actual view coord */
int x1 = s_box.x;
int y1 = s_box.y;
int x2 = s_box.x + s_box.width;
int y2 = s_box.y + s_box.height;
/* usable coord */
struct kywc_box *usable = &output->usable_area;
int ux1 = usable->x;
int uy1 = usable->y;
int ux2 = usable->x + usable->width;
int uy2 = usable->y + usable->height;
/* window edge adsorption in left, right */
if (abs(ux1 - x1) < *gap_x) {
*gap_x = abs(ux1 - x1);
pending->x = ux1 + kywc_view->margin.off_x;
} else if (abs(x2 - ux2) < *gap_x) {
*gap_x = abs(x2 - ux2);
pending->x = ux2 - pending->width - kywc_view->margin.off_width + kywc_view->margin.off_x;
}
/* top and bottom */
if (abs(uy1 - y1) < *gap_y) {
*gap_y = abs(uy1 - y1);
pending->y = uy1 + kywc_view->margin.off_y;
} else if (abs(y2 - uy2) < *gap_y) {
pending->y = uy2 - pending->height - kywc_view->margin.off_height + kywc_view->margin.off_y;
}
}
void window_move_constraints(struct kywc_view *kywc_view, struct output *output, int *x, int *y,
int width, int height)
{
if (kywc_view->unconstrained) {
return;
}
struct kywc_box pending = { *x, *y, width, height };
int gap_x = EDGE_OFFSET, gap_y = EDGE_OFFSET;
uint32_t edges = KYWC_EDGE_NONE;
/* window edge adsorption in left, right */
edges |= *x != kywc_view->geometry.x ? KYWC_EDGE_LEFT | KYWC_EDGE_RIGHT : KYWC_EDGE_NONE;
/* top and bottom */
edges |= *y != kywc_view->geometry.y ? KYWC_EDGE_TOP | KYWC_EDGE_BOTTOM : KYWC_EDGE_NONE;
window_adsorb_window_constraints(kywc_view, &pending, &gap_x, &gap_y, edges,
INTERACTIVE_MODE_MOVE);
gap_x = gap_x < EDGE_OFFSET ? gap_x : VIEW_EDGE_GAP;
gap_y = gap_y < EDGE_OFFSET ? gap_y : VIEW_EDGE_GAP;
window_adsorb_edges_constraints(kywc_view, &pending, output, &gap_x, &gap_y);
*x = pending.x;
*y = pending.y;
/* actual view coord */
int x1 = *x - kywc_view->margin.off_x;
int y1 = *y - kywc_view->margin.off_y;
int x2 = x1 + width + kywc_view->margin.off_width;
int y2 = y1 + height + kywc_view->margin.off_height;
/* get current seat constraints output */
struct kywc_box *usable = &output->usable_area;
int ux1 = usable->x, uy1 = usable->y;
int ux2 = usable->x + usable->width, uy2 = usable->y + usable->height;
struct kywc_box *geo = &output->geometry;
int top = uy1 - geo->y, bottom = geo->y + geo->height - uy2;
int left = ux1 - geo->x, right = geo->x + geo->width - ux2;
int bottom_gap = MIN(VIEW_BOTTOM_GAP, height);
int left_gap = MIN(VIEW_LEFT_GAP, width);
int right_gap = MIN(VIEW_RIGHT_GAP, width);
/* constraints when moving to top and bottom */
if (output_at_layout_edge(output, LAYOUT_EDGE_TOP) && height > top && y1 < uy1) {
*y = uy1 + kywc_view->margin.off_y;
} else if (output_at_layout_edge(output, LAYOUT_EDGE_BOTTOM) && height > bottom && y2 > uy2 &&
uy2 - y1 < bottom_gap) {
*y = uy2 - bottom_gap + kywc_view->margin.off_y;
}
/* constraints when moving to left and right */
if (output_at_layout_edge(output, LAYOUT_EDGE_LEFT) && width > left && x1 < ux1 &&
x2 - ux1 < left_gap) {
*x = left_gap - width - (kywc_view->margin.off_width - kywc_view->margin.off_x);
} else if (output_at_layout_edge(output, LAYOUT_EDGE_RIGHT) && width > right && x2 > ux2 &&
ux2 - x1 < right_gap) {
*x = ux2 - right_gap + kywc_view->margin.off_y;
}
}
static void interactive_process_move(struct interactive_grab *grab, double x, double y)
{
struct kywc_view *kywc_view = &grab->view->base;
struct kywc_box *geometry = &kywc_view->geometry;
if (kywc_view->maximized || kywc_view->tiled) {
struct kywc_box *saved = &grab->view->saved.geometry;
double frac = (x - geometry->x) / geometry->width;
int position_x = x - frac * saved->width;
if (position_x < geometry->x) {
position_x = geometry->x;
}
grab->geo.x = position_x;
if (kywc_view->maximized) {
kywc_view_set_maximized(kywc_view, false, NULL);
} else {
kywc_view_set_tiled(kywc_view, KYWC_TILE_NONE, NULL);
}
if (grab->proxy) {
int width = kywc_view->margin.off_width + saved->width;
int height = kywc_view->margin.off_height + saved->height;
move_proxy_resize(grab->proxy, width, height);
}
}
int nx = grab->geo.x + x - grab->cursor_x;
int ny = grab->geo.y + y - grab->cursor_y;
window_move_constraints(&grab->view->base, grab->output, &nx, &ny, geometry->width,
geometry->height);
if (grab->proxy) {
move_proxy_move(grab->proxy, nx, ny);
} else {
kywc_view_move(kywc_view, nx, ny);
}
interactive_move_show_snap_box(grab, x, y);
}
void interactive_resize_constraints(struct view *view, struct output *output, struct kywc_box *box,
uint32_t edges)
{
struct kywc_view *kywc_view = &view->base;
if (kywc_view->unconstrained) {
return;
}
/* get current seat constraints output */
struct kywc_box *usable = &output->usable_area;
struct kywc_box *current = &kywc_view->geometry;
/* pending view coord */
int x1 = box->x - kywc_view->margin.off_x;
int y1 = box->y - kywc_view->margin.off_y;
int x2 = x1 + box->width + kywc_view->margin.off_width;
int y2 = y1 + box->height + kywc_view->margin.off_height;
int gap_x = EDGE_OFFSET, gap_y = EDGE_OFFSET;
window_adsorb_window_constraints(kywc_view, box, &gap_x, &gap_y, edges,
INTERACTIVE_MODE_RESIZE);
if (x1 > usable->x + usable->width || x2 < usable->x || y1 > usable->y + usable->height ||
y2 < usable->y) {
usable = &output_from_kywc_output(view->output)->usable_area;
}
int ux2 = usable->x + usable->width;
int uy2 = usable->y + usable->height;
/* constraints when resize to top and bottom */
if (edges & KYWC_EDGE_TOP) {
/* top */
if (output_at_layout_edge(output, LAYOUT_EDGE_TOP) && y1 < usable->y) {
box->y = usable->y + kywc_view->margin.off_y;
box->height = current->height + current->y - box->y;
/* bottom */
} else if (output_at_layout_edge(output, LAYOUT_EDGE_BOTTOM) &&
y1 > uy2 - kywc_view->margin.off_y - VIEW_EDGE_GAP) {
box->y = uy2 - VIEW_EDGE_GAP;
box->height = y2 - box->y - (kywc_view->margin.off_height - kywc_view->margin.off_y);
}
} else if (edges & KYWC_EDGE_BOTTOM) {
/* top */
if (output_at_layout_edge(output, LAYOUT_EDGE_TOP) && y2 < usable->y + VIEW_EDGE_GAP) {
box->height = usable->y + VIEW_EDGE_GAP - y1 - kywc_view->margin.off_height;
/* bottom */
} else if (output_at_layout_edge(output, LAYOUT_EDGE_BOTTOM) && y2 > uy2) {
box->height = uy2 - y1 - kywc_view->margin.off_height;
}
}
/* constraints when resize to left and right */
if (edges & KYWC_EDGE_LEFT) {
/* left */
if (output_at_layout_edge(output, LAYOUT_EDGE_LEFT) && x1 < usable->x) {
box->x = usable->x + kywc_view->margin.off_x;
box->width = current->width + current->x - box->x;
/* right */
} else if (output_at_layout_edge(output, LAYOUT_EDGE_RIGHT) && x1 > ux2 - VIEW_EDGE_GAP) {
box->x = ux2 - VIEW_EDGE_GAP + kywc_view->margin.off_x;
box->width = x2 - box->x - (kywc_view->margin.off_width - kywc_view->margin.off_x);
}
} else if (edges & KYWC_EDGE_RIGHT) {
/* left */
if (output_at_layout_edge(output, LAYOUT_EDGE_LEFT) && x2 < usable->x + VIEW_EDGE_GAP) {
box->width = usable->x + VIEW_EDGE_GAP - x1 - kywc_view->margin.off_width;
/* right */
} else if (output_at_layout_edge(output, LAYOUT_EDGE_RIGHT) && x2 > ux2) {
box->width = ux2 - x1 - kywc_view->margin.off_width;
}
}
}
static void interactive_process_resize(struct interactive_grab *grab, double x, double y)
{
struct kywc_view *kywc_view = &grab->view->base;
int min_width = kywc_view->min_width;
int min_height = kywc_view->min_height;
if (!kywc_view->unconstrained) {
min_width = MAX(kywc_view->min_width, VIEW_MIN_WIDTH);
min_height = MAX(kywc_view->min_height, VIEW_MIN_HEIGHT);
}
int max_width = kywc_view->max_width;
int max_height = kywc_view->max_height;
int width = 0, height = 0;
output_layout_get_size(&width, &height);
if (max_width <= 0 || max_width > width - kywc_view->margin.off_width) {
max_width = width - kywc_view->margin.off_width;
}
if (max_height <= 0 || max_height > height - kywc_view->margin.off_height) {
max_height = height - kywc_view->margin.off_height;
}
struct kywc_box pending = kywc_view->geometry;
double dx = x - grab->cursor_x;
double dy = y - grab->cursor_y;
if (grab->resize_edges & KYWC_EDGE_TOP) {
pending.height = grab->geo.height - dy;
} else if (grab->resize_edges & KYWC_EDGE_BOTTOM) {
pending.height = grab->geo.height + dy;
}
pending.height = CLAMP(pending.height, min_height, max_height);
if (grab->resize_edges & KYWC_EDGE_LEFT) {
pending.width = grab->geo.width - dx;
} else if (grab->resize_edges & KYWC_EDGE_RIGHT) {
pending.width = grab->geo.width + dx;
}
pending.width = CLAMP(pending.width, min_width, max_width);
if (grab->resize_edges & KYWC_EDGE_TOP) {
/* anchor bottom edge */
pending.y = grab->geo.y + grab->geo.height - pending.height;
}
if (grab->resize_edges & KYWC_EDGE_LEFT) {
/* anchor right edge */
pending.x = grab->geo.x + grab->geo.width - pending.width;
}
interactive_resize_constraints(grab->view, grab->output, &pending, grab->resize_edges);
kywc_view_resize(kywc_view, &pending);
}
static void interactive_tile_output_update(struct interactive_grab *grab, int key,
enum tile_state current)
{
grab->output = output_from_kywc_output(grab->view->output);
enum layout_edge layout_edge = LAYOUT_EDGE_TOP;
if (key == KEY_LEFT) {
if (current == TILE_LEFT || current == TILE_TOP_LEFT || current == TILE_BOTTOM_LEFT) {
layout_edge = LAYOUT_EDGE_LEFT;
}
} else if (key == KEY_RIGHT) {
if (current == TILE_RIGHT || current == TILE_TOP_RIGHT || current == TILE_BOTTOM_RIGHT) {
layout_edge = LAYOUT_EDGE_RIGHT;
}
}
if (layout_edge == LAYOUT_EDGE_TOP) {
return;
}
if (!output_state_is_mirror_mode()) {
struct output *output = output_find_specified_output(grab->output, layout_edge);
if (!output) {
return;
}
view_fix_geometry(grab->view, &grab->view->saved.geometry, &grab->output->usable_area,
&output->usable_area);
grab->output = output;
}
}
static void interactive_tile_update(struct interactive_grab *grab, struct output *output, int state)
{
if (state == TILE_MINIMIZE) {
kywc_view_set_minimized(&grab->view->base, true);
return;
}
if (state == TILE_MAXIMIZE) {
kywc_view_set_maximized(&grab->view->base, true, NULL);
return;
}
if (grab->view->base.maximized && !state) {
kywc_view_set_maximized(&grab->view->base, false, NULL);
return;
}
if (grab->view->base.minimized) {
/* kywc_view_activate will call the minimize restore interface. */
kywc_view_activate(&grab->view->base);
view_set_focus(grab->view, grab->view->base.focused_seat);
return;
}
kywc_view_set_tiled(&grab->view->base, state, &output->base);
}
static int interactive_get_tile_state(struct view *view)
{
if (view->base.maximized) {
return TILE_MAXIMIZE;
} else if (view->base.minimized) {
return TILE_MINIMIZE;
}
return view->base.tiled < KYWC_TILE_CENTER ? (enum tile_state)view->base.tiled : TILE_NONE;
}
static void interactive_process_tile(struct interactive_grab *grab, int key)
{
if (key != KEY_UP && key != KEY_DOWN && key != KEY_LEFT && key != KEY_RIGHT) {
return;
}
enum tile_state pending_state = TILE_NONE;
enum tile_state current = interactive_get_tile_state(grab->view);
if (key == KEY_UP) {
pending_state = tile_states[current].key_up;
} else if (key == KEY_DOWN) {
pending_state = tile_states[current].key_down;
} else if (key == KEY_LEFT) {
pending_state = tile_states[current].key_left;
} else if (key == KEY_RIGHT) {
pending_state = tile_states[current].key_right;
}
interactive_tile_output_update(grab, key, current);
interactive_tile_update(grab, grab->output, pending_state);
}
static void interactive_process_tile_half_screen(struct interactive_grab *grab, int key)
{
if (key != KEY_UP && key != KEY_DOWN && key != KEY_LEFT && key != KEY_RIGHT) {
return;
}
enum tile_state current = interactive_get_tile_state(grab->view);
if (current == TILE_MINIMIZE) {
return;
}
grab->output = output_from_kywc_output(grab->view->output);
enum kywc_tile tile = KYWC_TILE_NONE;
if (key == KEY_UP) {
tile = KYWC_TILE_TOP;
} else if (key == KEY_DOWN) {
tile = KYWC_TILE_BOTTOM;
} else if (key == KEY_LEFT) {
tile = KYWC_TILE_LEFT;
} else if (key == KEY_RIGHT) {
tile = KYWC_TILE_RIGHT;
}
if (grab->view->base.tiled != tile) {
kywc_view_set_tiled(&grab->view->base, tile, grab->view->output);
return;
}
if (tile == KYWC_TILE_TOP || tile == KYWC_TILE_BOTTOM) {
return;
}
if (!output_state_is_mirror_mode()) {
enum layout_edge edge = tile == KYWC_TILE_LEFT ? LAYOUT_EDGE_LEFT : LAYOUT_EDGE_RIGHT;
struct output *output = output_find_specified_output(grab->output, edge);
if (!output) {
return;
}
view_fix_geometry(grab->view, &grab->view->saved.geometry, &grab->output->usable_area,
&output->usable_area);
grab->output = output;
}
tile = tile == KYWC_TILE_LEFT ? KYWC_TILE_RIGHT : KYWC_TILE_LEFT;
kywc_view_set_tiled(&grab->view->base, tile, &grab->output->base);
}
static bool interactive_move_filter(struct interactive_grab *grab, double x, double y)
{
if (grab->ongoing) {
return false;
}
struct kywc_view *kywc_view = &grab->view->base;
if (kywc_view->maximized || kywc_view->tiled) {
/* add move filter */
if (fabs(grab->cursor_x - x) < VIEW_MOVE_STEP &&
fabs(grab->cursor_y - y) < VIEW_MOVE_STEP) {
return true;
}
}
return false;
}
static bool interactive_resize_filter(struct interactive_grab *grab, uint32_t time)
{
if (time - grab->last_time < view_manager_get_resize_filter(grab->view)) {
return true;
}
grab->last_time = time;
return false;
}
static bool pointer_grab_motion(struct seat_pointer_grab *pointer_grab, uint32_t time, double lx,
double ly)
{
struct interactive_grab *grab = pointer_grab->data;
grab->output = input_current_output(grab->seat);
if (grab->mode == INTERACTIVE_MODE_MOVE) {
if (interactive_move_filter(grab, lx, ly)) {
return true;
}
/* set moving cursor image if moving */
if (!grab->ongoing) {
cursor_set_image(grab->seat->cursor, CURSOR_MOVE);
grab->view->interactive_moving = true;
grab->ongoing = true;
}
interactive_process_move(grab, lx, ly);
} else if (grab->mode == INTERACTIVE_MODE_RESIZE) {
if (interactive_resize_filter(grab, time)) {
return true;
}
if (!grab->ongoing) {
grab->view->current_resize_edges = grab->resize_edges;
grab->ongoing = true;
}
if (grab->has_linkage) {
tile_linkage_view_resize(grab->view, KYWC_EDGE_NONE, lx, ly);
} else {
interactive_process_resize(grab, lx, ly);
}
}
return true;
}
static bool pointer_grab_button(struct seat_pointer_grab *pointer_grab, uint32_t time,
uint32_t button, bool pressed)
{
struct interactive_grab *grab = pointer_grab->data;
if (!pressed && button == BTN_LEFT) {
bool on_surface = grab->seat->cursor->focus.node &&
wlr_surface_try_from_node(grab->seat->cursor->focus.node);
interactive_done(grab);
return !on_surface;
}
return true;
}
static bool pointer_grab_axis(struct seat_pointer_grab *pointer_grab, uint32_t time, bool vertical,
double value)
{
return true;
}
static void pointer_grab_cancel(struct seat_pointer_grab *pointer_grab)
{
struct interactive_grab *grab = pointer_grab->data;
interactive_grab_destroy(grab);
}
static const struct seat_pointer_grab_interface pointer_grab_impl = {
.motion = pointer_grab_motion,
.button = pointer_grab_button,
.axis = pointer_grab_axis,
.cancel = pointer_grab_cancel,
};
static bool keyboard_grab_key(struct seat_keyboard_grab *keyboard_grab, struct keyboard *keyboard,
uint32_t time, uint32_t key, bool pressed, uint32_t modifiers)
{
struct interactive_grab *grab = keyboard_grab->data;
if (!pressed) {
if (key != KEY_LEFTMETA && key != KEY_RIGHTMETA) {
return true;
}
if (grab->mode == INTERACTIVE_MODE_TILE ||
grab->mode == INTERACTIVE_MODE_TILE_HALF_SCREEN) {
interactive_done(grab);
return true;
}
}
if (grab->mode == INTERACTIVE_MODE_TILE || grab->mode == INTERACTIVE_MODE_TILE_HALF_SCREEN) {
if (!((WLR_MODIFIER_LOGO | WLR_MODIFIER_ALT) ^ modifiers)) {
interactive_process_tile_half_screen(grab, key);
} else if (!(WLR_MODIFIER_LOGO ^ modifiers)) {
interactive_process_tile(grab, key);
}
return true;
}
int step = grab->mode == INTERACTIVE_MODE_MOVE ? VIEW_MOVE_STEP : VIEW_RESIZE_STEP;
int dx = key == KEY_RIGHT ? step : (key == KEY_LEFT ? -step : 0);
int dy = key == KEY_DOWN ? step : (key == KEY_UP ? -step : 0);
/* restore to the orig geometry */
if (key == KEY_ESC) {
struct kywc_view *kywc_view = &grab->view->base;
struct kywc_box geo = grab->geo;
if (grab->has_linkage) {
tile_linkage_resize_done(grab->view, true);
grab->has_linkage = false;
}
interactive_done(grab);
kywc_view_resize(kywc_view, &geo);
return false;
}
if (key == KEY_ENTER) {
interactive_done(grab);
return false;
}
struct cursor *cursor = grab->seat->cursor;
cursor_move(cursor, NULL, dx, dy, true, false);
pointer_grab_motion(&grab->pointer_grab, time, cursor->lx, cursor->ly);
return true;
}
static void keyboard_grab_cancel(struct seat_keyboard_grab *keyboard_grab)
{
struct interactive_grab *grab = keyboard_grab->data;
interactive_grab_destroy(grab);
}
static const struct seat_keyboard_grab_interface keyboard_grab_impl = {
.key = keyboard_grab_key,
.cancel = keyboard_grab_cancel,
};
static bool touch_grab_touch(struct seat_touch_grab *touch_grab, uint32_t time, bool down)
{
if (down) {
return true;
}
struct interactive_grab *grab = touch_grab->data;
/**
* When touching on a surface, the pointer event is not simulated and the focus is not updated.
* It will call the motion of the pointer and update the hover.
* Use hover to determine whether it is on the surface.
* */
bool on_surface =
grab->seat->cursor->hover.node && wlr_surface_try_from_node(grab->seat->cursor->hover.node);
interactive_done(grab);
return !on_surface;
}
static bool touch_grab_motion(struct seat_touch_grab *touch_grab, uint32_t time, double lx,
double ly)
{
struct interactive_grab *grab = touch_grab->data;
bool first = !grab->ongoing;
pointer_grab_motion(&grab->pointer_grab, time, lx, ly);
if (first && grab->ongoing) {
seat_reset_input_gesture(grab->seat);
}
return true;
}
static void touch_grab_cancel(struct seat_touch_grab *touch_grab)
{
struct interactive_grab *grab = touch_grab->data;
interactive_grab_destroy(grab);
}
static const struct seat_touch_grab_interface touch_grab_impl = {
.touch = touch_grab_touch,
.motion = touch_grab_motion,
.cancel = touch_grab_cancel,
};
static void handle_view_unmap(struct wl_listener *listener, void *data)
{
struct interactive_grab *grab = wl_container_of(listener, grab, view_unmap);
interactive_done(grab);
}
static void handle_move_proxy_destroy(struct wl_listener *listener, void *data)
{
struct interactive_grab *grab = wl_container_of(listener, grab, proxy_destroy);
wl_list_remove(&grab->proxy_destroy.link);
grab->proxy = NULL;
}
static int interactive_tiled_key_by_action(int action)
{
int key = 0;
if (action == WINDOW_ACTION_TILE_TOP || action == WINDOW_ACTION_TILE_TOP_HALF_SCREEN) {
key = KEY_UP;
} else if (action == WINDOW_ACTION_TILE_BOTTOM ||
action == WINDOW_ACTION_TILE_BOTTOM_HALF_SCREEN) {
key = KEY_DOWN;
} else if (action == WINDOW_ACTION_TILE_LEFT || action == WINDOW_ACTION_TILE_LEFT_HALF_SCREEN) {
key = KEY_LEFT;
} else if (action == WINDOW_ACTION_TILE_RIGHT ||
action == WINDOW_ACTION_TILE_RIGHT_HALF_SCREEN) {
key = KEY_RIGHT;
}
return key;
}
static void interactive_grab_add(struct view *view, enum interactive_mode mode, uint32_t edges,
struct seat *seat)
{
struct interactive_grab *grab = calloc(1, sizeof(*grab));
if (!grab) {
return;
}
grab->pointer_grab = (struct seat_pointer_grab){ &pointer_grab_impl, seat, grab };
seat_start_pointer_grab(seat, &grab->pointer_grab);
grab->keyboard_grab = (struct seat_keyboard_grab){ &keyboard_grab_impl, seat, grab };
seat_start_keyboard_grab(seat, &grab->keyboard_grab);
grab->touch_grab = (struct seat_touch_grab){ &touch_grab_impl, seat, grab };
seat_start_touch_grab(seat, &grab->touch_grab);
grab->seat = seat;
grab->mode = mode;
grab->cursor_x = seat->cursor->lx;
grab->cursor_y = seat->cursor->ly;
grab->geo = view_action_change_size(view->pending.configure_action)
? view->pending.configure_geometry
: view->base.geometry;
grab->resize_edges = edges;
grab->ongoing = false;
grab->view = view;
grab->view_unmap.notify = handle_view_unmap;
wl_signal_add(&view->base.events.unmap, &grab->view_unmap);
grab->proxy_destroy.notify = handle_move_proxy_destroy;
wl_list_init(&grab->proxy_destroy.link);
/* set the default cursor */
if (mode == INTERACTIVE_MODE_MOVE) {
grab->saved_usable_area = output_from_kywc_output(view->output)->usable_area;
grab->update_saved_geo = !view->base.tiled && !view->base.maximized;
// In order not to rebase after the configure_action
if (view_action_change_size(view->pending.configure_action)) {
grab->view->interactive_moving = true;
// recalculate the coordinates of the window under the cursor
grab->geo.x = grab->cursor_x -
((grab->cursor_x - view->base.geometry.x) / view->base.geometry.width) *
grab->geo.width;
grab->geo.y = grab->cursor_y -
((grab->cursor_y - view->base.geometry.y) / view->base.geometry.height) *
grab->geo.height;
}
cursor_set_image(seat->cursor, CURSOR_DEFAULT);
/* create a move proxy for move effect */
int width = view->base.geometry.width + view->base.margin.off_width;
int height = view->base.geometry.height + view->base.margin.off_height;
grab->proxy = move_proxy_create(view, width, height);
if (grab->proxy) {
move_proxy_add_destroy_listener(grab->proxy, &grab->proxy_destroy);
}
} else if (mode == INTERACTIVE_MODE_RESIZE) {
grab->has_linkage =
tile_linkage_view_resize(view, edges, seat->cursor->lx, seat->cursor->ly);
cursor_set_resize_image(seat->cursor, edges);
} else if (mode == INTERACTIVE_MODE_TILE) {
int key = interactive_tiled_key_by_action(edges);
interactive_process_tile(grab, key);
} else if (mode == INTERACTIVE_MODE_TILE_HALF_SCREEN) {
int key = interactive_tiled_key_by_action(edges);
interactive_process_tile_half_screen(grab, key);
}
struct wl_event_loop *loop = wl_display_get_event_loop(seat->wlr_seat->display);
grab->filter = wl_event_loop_add_timer(loop, handle_snap_box, grab);
}
void window_begin_move(struct view *view, struct seat *seat)
{
if (!KYWC_VIEW_IS_MOVABLE(&view->base)) {
return;
}
interactive_grab_add(view, INTERACTIVE_MODE_MOVE, 0, seat);
}
void window_begin_resize(struct view *view, uint32_t edges, struct seat *seat)
{
if (view->base.maximized || !KYWC_VIEW_IS_RESIZABLE(&view->base)) {
return;
}
interactive_grab_add(view, INTERACTIVE_MODE_RESIZE, edges, seat);
}
void window_begin_tile(struct view *view, uint32_t key, struct seat *seat)
{
if (view->base.fullscreen || view_manager_get_highlight()) {
return;
}
interactive_grab_add(view, INTERACTIVE_MODE_TILE, key, seat);
}
void window_begin_tile_half_screen(struct view *view, uint32_t key, struct seat *seat)
{
if (view->base.fullscreen || view_manager_get_highlight()) {
return;
}
interactive_grab_add(view, INTERACTIVE_MODE_TILE_HALF_SCREEN, key, seat);
}
kylin-wayland-compositor/src/view/kde_virtual_desktop.c 0000664 0001750 0001750 00000025604 15160461067 022443 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include "plasma-virtual-desktop-protocol.h"
#include "view/workspace.h"
#include "view_p.h"
#define VIRTUAL_DESKTOP_VERSION 1
#define VIRTUAL_DESKTOP_MANAGEMENT_VERSION 2
struct kde_virtual_desktop_management {
struct wl_global *global;
struct wl_list resources;
struct wl_list virtual_desktops;
struct wl_listener new_workspace;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct kde_virtual_desktop {
struct wl_list link;
/* request get_virtual_desktop */
struct wl_list resources;
struct workspace *workspace;
struct kde_virtual_desktop_management *management;
struct wl_listener activate;
struct wl_listener destroy;
};
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct kde_virtual_desktop_management *management =
wl_container_of(listener, management, server_destroy);
wl_list_remove(&management->server_destroy.link);
free(management);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
struct kde_virtual_desktop_management *management =
wl_container_of(listener, management, display_destroy);
wl_list_remove(&management->new_workspace.link);
wl_list_remove(&management->display_destroy.link);
wl_global_destroy(management->global);
}
static void handle_workspace_destroy(struct wl_listener *listener, void *data)
{
struct kde_virtual_desktop *virtual_desktop =
wl_container_of(listener, virtual_desktop, destroy);
wl_list_remove(&virtual_desktop->destroy.link);
wl_list_remove(&virtual_desktop->activate.link);
wl_list_remove(&virtual_desktop->link);
struct kde_virtual_desktop_management *management = virtual_desktop->management;
struct wl_resource *resource;
wl_resource_for_each(resource, &virtual_desktop->resources) {
org_kde_plasma_virtual_desktop_send_removed(resource);
}
wl_resource_for_each(resource, &management->resources) {
org_kde_plasma_virtual_desktop_management_send_desktop_removed(
resource, virtual_desktop->workspace->uuid);
}
free(virtual_desktop);
}
static void handle_workspace_activate(struct wl_listener *listener, void *data)
{
struct kde_virtual_desktop *virtual_desktop =
wl_container_of(listener, virtual_desktop, activate);
struct wl_resource *resource;
wl_resource_for_each(resource, &virtual_desktop->resources) {
if (virtual_desktop->workspace->activated) {
org_kde_plasma_virtual_desktop_send_activated(resource);
} else {
org_kde_plasma_virtual_desktop_send_deactivated(resource);
}
org_kde_plasma_virtual_desktop_send_done(resource);
}
}
static void handle_new_workspace(struct wl_listener *listener, void *data)
{
struct kde_virtual_desktop *virtual_desktop = calloc(1, sizeof(*virtual_desktop));
if (!virtual_desktop) {
return;
}
struct workspace *workspace = data;
virtual_desktop->workspace = workspace;
virtual_desktop->activate.notify = handle_workspace_activate;
wl_signal_add(&workspace->events.activate, &virtual_desktop->activate);
virtual_desktop->destroy.notify = handle_workspace_destroy;
wl_signal_add(&workspace->events.destroy, &virtual_desktop->destroy);
struct kde_virtual_desktop_management *management =
wl_container_of(listener, management, new_workspace);
virtual_desktop->management = management;
wl_list_init(&virtual_desktop->resources);
wl_list_insert(&management->virtual_desktops, &virtual_desktop->link);
struct wl_resource *resource;
wl_resource_for_each(resource, &management->resources) {
org_kde_plasma_virtual_desktop_management_send_desktop_created(
resource, workspace->uuid, virtual_desktop->workspace->position);
}
}
static struct kde_virtual_desktop *
get_virtual_desktop(struct kde_virtual_desktop_management *management, const char *uuid)
{
struct kde_virtual_desktop *virtual_desktop;
wl_list_for_each(virtual_desktop, &management->virtual_desktops, link) {
if (!strcmp(uuid, virtual_desktop->workspace->uuid)) {
return virtual_desktop;
}
}
return NULL;
}
static void handle_request_activate(struct wl_client *client, struct wl_resource *resource)
{
struct kde_virtual_desktop *virtual_desktop = wl_resource_get_user_data(resource);
if (virtual_desktop) {
workspace_activate(virtual_desktop->workspace);
}
}
static const struct org_kde_plasma_virtual_desktop_interface kde_virtual_desktop_impl = {
.request_activate = handle_request_activate,
};
static void kde_virtual_desktop_handle_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
}
static void handle_get_virtual_desktop(struct wl_client *client,
struct wl_resource *management_resource, uint32_t id,
const char *desktop_id)
{
struct wl_resource *resource = wl_resource_create(
client, &org_kde_plasma_virtual_desktop_interface, VIRTUAL_DESKTOP_VERSION, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &kde_virtual_desktop_impl, NULL,
kde_virtual_desktop_handle_resource_destroy);
wl_list_init(wl_resource_get_link(resource));
struct kde_virtual_desktop_management *management =
wl_resource_get_user_data(management_resource);
struct kde_virtual_desktop *virtual_desktop = get_virtual_desktop(management, desktop_id);
if (!virtual_desktop) {
/* there is no error code defined in protocol */
org_kde_plasma_virtual_desktop_send_removed(resource);
return;
}
wl_resource_set_user_data(resource, virtual_desktop);
wl_list_insert(&virtual_desktop->resources, wl_resource_get_link(resource));
/* send all desktop info to client */
org_kde_plasma_virtual_desktop_send_desktop_id(resource, virtual_desktop->workspace->uuid);
org_kde_plasma_virtual_desktop_send_name(resource, virtual_desktop->workspace->name);
if (virtual_desktop->workspace->activated) {
org_kde_plasma_virtual_desktop_send_activated(resource);
} else {
org_kde_plasma_virtual_desktop_send_deactivated(resource);
}
org_kde_plasma_virtual_desktop_send_done(resource);
}
static void handle_request_create_virtual_desktop(struct wl_client *client,
struct wl_resource *resource, const char *name,
uint32_t position)
{
workspace_create(name, position);
}
static void handle_request_remove_virtual_desktop(struct wl_client *client,
struct wl_resource *resource,
const char *desktop_id)
{
struct kde_virtual_desktop_management *management = wl_resource_get_user_data(resource);
struct kde_virtual_desktop *virtual_desktop = get_virtual_desktop(management, desktop_id);
if (!virtual_desktop) {
/* there is no error code defined in protocol */
return;
}
if (wl_list_length(&management->virtual_desktops) == 1) {
kywc_log(KYWC_WARN, "Reject to destroy the last virtual desktop");
return;
}
workspace_destroy(virtual_desktop->workspace);
}
static const struct org_kde_plasma_virtual_desktop_management_interface
kde_virtual_desktop_management_impl = {
.get_virtual_desktop = handle_get_virtual_desktop,
.request_create_virtual_desktop = handle_request_create_virtual_desktop,
.request_remove_virtual_desktop = handle_request_remove_virtual_desktop,
};
static void kde_virtual_desktop_management_handle_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
}
static struct kde_virtual_desktop *
get_virtual_desktop_by_workspace(struct kde_virtual_desktop_management *management,
struct workspace *workspace)
{
struct kde_virtual_desktop *virtual_desktop;
wl_list_for_each(virtual_desktop, &management->virtual_desktops, link) {
if (virtual_desktop->workspace == workspace) {
return virtual_desktop;
}
}
return NULL;
}
static void kde_virtual_desktop_management_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id)
{
struct wl_resource *resource = wl_resource_create(
client, &org_kde_plasma_virtual_desktop_management_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
struct kde_virtual_desktop_management *management = data;
wl_resource_set_implementation(resource, &kde_virtual_desktop_management_impl, management,
kde_virtual_desktop_management_handle_resource_destroy);
wl_list_insert(&management->resources, wl_resource_get_link(resource));
/* send all desktops to client when bind */
struct kde_virtual_desktop *virtual_desktop;
for (uint32_t i = 0; i < workspace_manager_get_count(); i++) {
virtual_desktop = get_virtual_desktop_by_workspace(management, workspace_by_position(i));
org_kde_plasma_virtual_desktop_management_send_desktop_created(
resource, virtual_desktop->workspace->uuid, virtual_desktop->workspace->position);
}
if (version >= ORG_KDE_PLASMA_VIRTUAL_DESKTOP_MANAGEMENT_ROWS_SINCE_VERSION) {
org_kde_plasma_virtual_desktop_management_send_rows(resource, workspace_manager_get_rows());
}
org_kde_plasma_virtual_desktop_management_send_done(resource);
}
bool kde_virtual_desktop_management_create(struct server *server)
{
struct kde_virtual_desktop_management *management = calloc(1, sizeof(*management));
if (!management) {
return false;
}
wl_list_init(&management->resources);
wl_list_init(&management->virtual_desktops);
management->global = wl_global_create(
server->display, &org_kde_plasma_virtual_desktop_management_interface,
VIRTUAL_DESKTOP_MANAGEMENT_VERSION, management, kde_virtual_desktop_management_bind);
if (!management->global) {
kywc_log(KYWC_WARN, "Kde plasma virtual desktop create failed");
free(management);
return false;
}
management->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &management->server_destroy);
management->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &management->display_destroy);
management->new_workspace.notify = handle_new_workspace;
workspace_manager_add_new_listener(&management->new_workspace);
return true;
}
kylin-wayland-compositor/src/view/xdg_toplevel_icon.c 0000664 0001750 0001750 00000026675 15160461067 022116 0 ustar feng feng // SPDX-FileCopyrightText: 2025 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include "output.h"
#include "theme.h"
#include "util/macros.h"
#include "view_p.h"
#include "xdg-toplevel-icon-v1-protocol.h"
#define XDG_TOPLEVEL_ICON_MANAGER_VERSION 1
struct xdg_toplevel_icon_manager {
struct wl_global *global;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct xdg_toplevel_icon_buffer {
struct wl_list link;
int scale;
struct wlr_buffer *wlr_buffer;
};
struct xdg_toplevel_icon {
char *name; // may be NULL
struct wl_list buffers; // xdg_toplevel_icon_buffer.link
int n_refs;
bool immutable;
};
struct xdg_icon_toplevel {
struct xdg_toplevel_icon *icon;
struct wlr_xdg_toplevel *toplevel;
struct wl_listener surface_commit;
struct wl_listener toplevel_destroy;
};
static const struct xdg_toplevel_icon_v1_interface icon_impl;
static struct xdg_toplevel_icon *icon_from_resource(struct wl_resource *resource)
{
assert(wl_resource_instance_of(resource, &xdg_toplevel_icon_v1_interface, &icon_impl));
return wl_resource_get_user_data(resource);
}
static void icon_destroy(struct xdg_toplevel_icon *icon)
{
struct xdg_toplevel_icon_buffer *icon_buffer, *tmp;
wl_list_for_each_safe(icon_buffer, tmp, &icon->buffers, link) {
wlr_buffer_unlock(icon_buffer->wlr_buffer);
wl_list_remove(&icon_buffer->link);
free(icon_buffer);
}
free(icon->name);
free(icon);
}
static void icon_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void icon_handle_set_name(struct wl_client *client, struct wl_resource *resource,
const char *name)
{
struct xdg_toplevel_icon *icon = icon_from_resource(resource);
if (!icon) {
return;
}
if (icon->immutable) {
wl_resource_post_error(
resource, XDG_TOPLEVEL_ICON_V1_ERROR_IMMUTABLE,
"the icon has already been assigned to a toplevel and must not be changed");
return;
}
char *tmp = strdup(name);
if (!tmp) {
wl_resource_post_no_memory(resource);
return;
}
free(icon->name);
icon->name = tmp;
}
static void icon_handle_add_buffer(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *buffer_resource, int32_t scale)
{
struct xdg_toplevel_icon *icon = icon_from_resource(resource);
if (!icon) {
return;
}
if (icon->immutable) {
wl_resource_post_error(
resource, XDG_TOPLEVEL_ICON_V1_ERROR_IMMUTABLE,
"the icon has already been assigned to a toplevel and must not be changed");
return;
}
struct wlr_buffer *wlr_buffer = wlr_buffer_try_from_resource(buffer_resource);
const char *bad_buffer_msg = NULL;
struct wlr_shm_attributes shm_attribs;
if (!wlr_buffer_get_shm(wlr_buffer, &shm_attribs)) {
bad_buffer_msg = "not backed by wl_shm";
} else if (wlr_buffer->width != wlr_buffer->height) {
bad_buffer_msg = "not square";
}
if (bad_buffer_msg) {
wl_resource_post_error(resource, XDG_TOPLEVEL_ICON_V1_ERROR_INVALID_BUFFER,
"the provided buffer does not satisfy requirements: %s",
bad_buffer_msg);
wlr_buffer_unlock(wlr_buffer);
return;
}
struct xdg_toplevel_icon_buffer *icon_buffer;
wl_list_for_each(icon_buffer, &icon->buffers, link) {
if (icon_buffer->wlr_buffer->width == wlr_buffer->width && icon_buffer->scale == scale) {
wlr_buffer_unlock(icon_buffer->wlr_buffer);
icon_buffer->wlr_buffer = wlr_buffer;
return;
}
}
icon_buffer = calloc(1, sizeof(*icon_buffer));
if (!icon_buffer) {
wl_resource_post_no_memory(resource);
}
icon_buffer->wlr_buffer = wlr_buffer;
icon_buffer->scale = scale;
wl_list_insert(&icon->buffers, &icon_buffer->link);
}
static const struct xdg_toplevel_icon_v1_interface icon_impl = {
.destroy = icon_handle_destroy,
.set_name = icon_handle_set_name,
.add_buffer = icon_handle_add_buffer,
};
static struct xdg_toplevel_icon *xdg_toplevel_icon_ref(struct xdg_toplevel_icon *icon)
{
++icon->n_refs;
return icon;
}
static void xdg_toplevel_icon_unref(struct xdg_toplevel_icon *icon)
{
if (icon == NULL) {
return;
}
assert(icon->n_refs > 0);
--icon->n_refs;
if (icon->n_refs == 0) {
icon_destroy(icon);
}
}
static void icon_handle_resource_destroy(struct wl_resource *resource)
{
xdg_toplevel_icon_unref(icon_from_resource(resource));
}
static void manager_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void manager_handle_create_icon(struct wl_client *client, struct wl_resource *resource,
uint32_t id)
{
struct xdg_toplevel_icon *icon = calloc(1, sizeof(*icon));
if (!icon) {
wl_client_post_no_memory(client);
return;
}
struct wl_resource *icon_resource = wl_resource_create(client, &xdg_toplevel_icon_v1_interface,
wl_resource_get_version(resource), id);
if (!icon_resource) {
wl_client_post_no_memory(client);
free(icon);
return;
}
icon->n_refs = 1;
wl_list_init(&icon->buffers);
wl_resource_set_implementation(icon_resource, &icon_impl, icon, icon_handle_resource_destroy);
}
static void icon_toplevel_handle_surface_commit(struct wl_listener *listener, void *data)
{
struct xdg_icon_toplevel *icon_toplevel =
wl_container_of(listener, icon_toplevel, surface_commit);
struct xdg_toplevel_icon *icon = icon_toplevel->icon;
struct wlr_xdg_toplevel *toplevel = icon_toplevel->toplevel;
// surface commit, the view must have a value
struct view *view = view_try_from_wlr_surface(toplevel->base->surface);
if (icon) {
view_set_icon_name(view, icon->name);
struct xdg_toplevel_icon_buffer *icon_buffer;
wl_list_for_each(icon_buffer, &icon->buffers, link) {
xdg_view_add_icon_buffer(toplevel->base, icon_buffer->scale, icon_buffer->wlr_buffer);
}
xdg_toplevel_icon_unref(icon);
} else {
view_set_icon_name(view, NULL);
xdg_view_clear_icon_buffer(toplevel->base);
}
view_set_icon(view, true);
wl_list_remove(&icon_toplevel->surface_commit.link);
wl_list_remove(&icon_toplevel->toplevel_destroy.link);
free(icon_toplevel);
}
static void icon_toplevel_handle_toplevel_destroy(struct wl_listener *listener, void *data)
{
struct xdg_icon_toplevel *icon_toplevel =
wl_container_of(listener, icon_toplevel, toplevel_destroy);
if (icon_toplevel->icon) {
xdg_toplevel_icon_unref(icon_toplevel->icon);
}
wl_list_remove(&icon_toplevel->surface_commit.link);
wl_list_remove(&icon_toplevel->toplevel_destroy.link);
free(icon_toplevel);
}
static void manager_handle_set_icon(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *toplevel_resource,
struct wl_resource *icon_resource)
{
struct wlr_xdg_toplevel *toplevel = wlr_xdg_toplevel_from_resource(toplevel_resource);
if (!toplevel) {
return;
}
struct xdg_icon_toplevel *icon_toplevel = calloc(1, sizeof(*icon_toplevel));
if (!icon_toplevel) {
wl_client_post_no_memory(client);
return;
}
struct xdg_toplevel_icon *icon = icon_resource ? icon_from_resource(icon_resource) : NULL;
if (icon) {
icon->immutable = true;
if (!icon->name && wl_list_empty(&icon->buffers)) {
// Same as supplying null icon
icon = NULL;
}
}
if (icon) {
icon_toplevel->icon = xdg_toplevel_icon_ref(icon);
}
icon_toplevel->toplevel = toplevel;
icon_toplevel->surface_commit.notify = icon_toplevel_handle_surface_commit;
wl_signal_add(&toplevel->base->surface->events.commit, &icon_toplevel->surface_commit);
icon_toplevel->toplevel_destroy.notify = icon_toplevel_handle_toplevel_destroy;
wl_signal_add(&toplevel->events.destroy, &icon_toplevel->toplevel_destroy);
}
static const struct xdg_toplevel_icon_manager_v1_interface xdg_toplevel_icon_manager_impl = {
.destroy = manager_handle_destroy,
.create_icon = manager_handle_create_icon,
.set_icon = manager_handle_set_icon,
};
static bool get_output_scale_icon_size(struct kywc_output *output, int index, void *data)
{
struct wl_array *sizes = data;
struct theme *theme = theme_manager_get_theme();
float theme_icon_size = theme->icon_size * output->state.scale;
uint32_t *size;
wl_array_for_each(size, sizes) {
if (FLOAT_EQUAL(*size, theme_icon_size)) {
return false;
}
}
uint32_t *icon_size = wl_array_add(sizes, sizeof(*icon_size));
if (icon_size) {
*icon_size = theme_icon_size;
}
return false;
}
static void xdg_toplevel_icon_manager_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct xdg_toplevel_icon_manager *manager = data;
struct wl_resource *resource =
wl_resource_create(client, &xdg_toplevel_icon_manager_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &xdg_toplevel_icon_manager_impl, manager, NULL);
struct wl_array sizes;
wl_array_init(&sizes);
output_manager_for_each_output(get_output_scale_icon_size, true, &sizes);
uint32_t *size;
wl_array_for_each(size, &sizes) {
xdg_toplevel_icon_manager_v1_send_icon_size(resource, *size);
}
wl_array_release(&sizes);
xdg_toplevel_icon_manager_v1_send_done(resource);
}
static void manager_handle_display_destroy(struct wl_listener *listener, void *data)
{
struct xdg_toplevel_icon_manager *manager = wl_container_of(listener, manager, display_destroy);
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
}
static void manager_handle_server_destroy(struct wl_listener *listener, void *data)
{
struct xdg_toplevel_icon_manager *manager = wl_container_of(listener, manager, server_destroy);
wl_list_remove(&manager->server_destroy.link);
free(manager);
}
bool xdg_toplevel_icon_manager_create(struct server *server)
{
struct xdg_toplevel_icon_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->global = wl_global_create(server->display, &xdg_toplevel_icon_manager_v1_interface,
XDG_TOPLEVEL_ICON_MANAGER_VERSION, manager,
xdg_toplevel_icon_manager_bind);
if (!manager->global) {
kywc_log(KYWC_WARN, "Xdg toplevel icon manager create failed");
free(manager);
return false;
}
manager->display_destroy.notify = manager_handle_display_destroy;
wl_display_add_destroy_listener(server->display, &manager->display_destroy);
manager->server_destroy.notify = manager_handle_server_destroy;
server_add_destroy_listener(server, &manager->server_destroy);
return true;
}
kylin-wayland-compositor/src/view/wlr_layer_shell.c 0000664 0001750 0001750 00000052365 15160461067 021574 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include "input/event.h"
#include "input/seat.h"
#include "output.h"
#include "scene/surface.h"
#include "view_p.h"
struct wlr_layer_shell_manager {
struct wl_list outputs;
/* layers for layer shell */
struct view_layer layers[4];
struct wl_listener new_output;
struct wl_listener new_surface;
struct wl_listener destroy;
};
struct layer_output {
struct wl_list link;
struct output *output;
/* layer shells in 4 layers */
struct wl_list shells[4];
/* if have exclusive_zone < 0 */
struct wl_listener geometry;
/* if have exclusive_zone == 0 */
struct wl_listener usable_area;
/* if have exclusive_zone > 0 */
struct wl_listener update_usable_area;
/* destroy wlr layer-shell if output off and destroy*/
struct wl_listener off;
struct wl_listener destroy;
};
struct layer_shell {
struct wl_list link;
struct ky_scene_tree *tree;
struct wlr_layer_surface_v1 *layer_surface;
struct wlr_seat_keyboard_grab keyboard_grab;
struct wl_listener commit;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener new_popup;
struct wl_listener destroy;
};
static struct wlr_layer_shell_manager *manager = NULL;
static void layer_shell_keyboard_enter(struct wlr_seat_keyboard_grab *grab,
struct wlr_surface *surface, const uint32_t keycodes[],
size_t num_keycodes,
const struct wlr_keyboard_modifiers *modifiers)
{
// keyboard focus should remain on the layer shell
}
static void layer_shell_keyboard_clear_focus(struct wlr_seat_keyboard_grab *grab)
{
// keyboard focus should remain on the layer shell
}
static void layer_shell_keyboard_key(struct wlr_seat_keyboard_grab *grab, uint32_t time,
uint32_t key, uint32_t state)
{
wlr_seat_keyboard_send_key(grab->seat, time, key, state);
}
static void layer_shell_keyboard_modifiers(struct wlr_seat_keyboard_grab *grab,
const struct wlr_keyboard_modifiers *modifiers)
{
wlr_seat_keyboard_send_modifiers(grab->seat, modifiers);
}
static void layer_shell_keyboard_cancel(struct wlr_seat_keyboard_grab *grab)
{
grab->seat = NULL;
}
static const struct wlr_keyboard_grab_interface layer_shell_keyboard_grab = {
.enter = layer_shell_keyboard_enter,
.clear_focus = layer_shell_keyboard_clear_focus,
.key = layer_shell_keyboard_key,
.modifiers = layer_shell_keyboard_modifiers,
.cancel = layer_shell_keyboard_cancel,
};
static void layer_shell_keyboard_interactivity(struct layer_shell *layer_shell, struct seat *seat)
{
struct wlr_layer_surface_v1 *layer_surface = layer_shell->layer_surface;
struct wlr_seat *wlr_seat = layer_shell->keyboard_grab.seat;
if (layer_surface->surface->mapped) {
if (layer_surface->current.keyboard_interactive) {
seat_focus_surface(seat, layer_surface->surface);
if (wlr_seat && wlr_seat != seat->wlr_seat) {
wlr_seat_keyboard_end_grab(wlr_seat);
}
/* start a seat keyboard grab */
wlr_seat = layer_shell->keyboard_grab.seat;
if (!wlr_seat && layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
layer_shell->keyboard_grab.interface = &layer_shell_keyboard_grab;
layer_shell->keyboard_grab.data = layer_shell;
wlr_seat_keyboard_start_grab(seat->wlr_seat, &layer_shell->keyboard_grab);
}
}
} else {
if (wlr_seat && wlr_seat->keyboard_state.grab == &layer_shell->keyboard_grab) {
wlr_seat_keyboard_end_grab(wlr_seat);
}
// XXX: auto focus layer_shell
if (seat->wlr_seat->keyboard_state.focused_surface == layer_surface->surface) {
view_activate_topmost(false);
}
}
}
static struct layer_output *layer_output_from_wlr_output(struct wlr_output *wlr_output)
{
struct layer_output *layer_output;
wl_list_for_each(layer_output, &manager->outputs, link) {
if (layer_output->output->wlr_output == wlr_output) {
return layer_output;
}
}
return NULL;
}
static void layer_surface_exclusive_zone(struct wlr_layer_surface_v1_state *state,
struct kywc_box *usable_area)
{
switch (state->anchor) {
case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP:
case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT):
// Anchor top
usable_area->y += state->exclusive_zone + state->margin.top;
usable_area->height -= state->exclusive_zone + state->margin.top;
break;
case ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM:
case (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT):
// Anchor bottom
usable_area->height -= state->exclusive_zone + state->margin.bottom;
break;
case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT:
case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT):
// Anchor left
usable_area->x += state->exclusive_zone + state->margin.left;
usable_area->width -= state->exclusive_zone + state->margin.left;
break;
case ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT:
case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT):
// Anchor right
usable_area->width -= state->exclusive_zone + state->margin.right;
break;
}
}
static void layer_shell_configure_surface(struct layer_shell *layer_shell,
const struct kywc_box *full_area,
struct kywc_box *usable_area)
{
struct wlr_layer_surface_v1 *layer_surface = layer_shell->layer_surface;
struct wlr_layer_surface_v1_state *state = &layer_surface->current;
// If the exclusive zone is set to -1, the layer surface will use the
// full area of the output, otherwise it is constrained to the
// remaining usable area.
struct kywc_box bounds;
if (state->exclusive_zone == -1) {
bounds = *full_area;
} else {
bounds = *usable_area;
}
struct kywc_box box = {
.width = state->desired_width,
.height = state->desired_height,
};
// Horizontal positioning
if (box.width == 0) {
box.x = bounds.x + state->margin.left;
box.width = bounds.width - (state->margin.left + state->margin.right);
} else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT &&
state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) {
box.x = bounds.x + bounds.width / 2 - box.width / 2;
} else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) {
box.x = bounds.x + state->margin.left;
} else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) {
box.x = bounds.x + bounds.width - box.width - state->margin.right;
} else {
box.x = bounds.x + bounds.width / 2 - box.width / 2;
}
// Vertical positioning
if (box.height == 0) {
box.y = bounds.y + state->margin.top;
box.height = bounds.height - (state->margin.top + state->margin.bottom);
} else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP &&
state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) {
box.y = bounds.y + bounds.height / 2 - box.height / 2;
} else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) {
box.y = bounds.y + state->margin.top;
} else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) {
box.y = bounds.y + bounds.height - box.height - state->margin.bottom;
} else {
box.y = bounds.y + bounds.height / 2 - box.height / 2;
}
ky_scene_node_set_position(&layer_shell->tree->node, box.x, box.y);
wlr_layer_surface_v1_configure(layer_surface, box.width, box.height);
if (layer_surface->surface->mapped && state->exclusive_zone > 0) {
layer_surface_exclusive_zone(state, usable_area);
}
}
static void layer_shell_handle_commit(struct wl_listener *listener, void *data)
{
struct layer_shell *layer_shell = wl_container_of(listener, layer_shell, commit);
struct wlr_layer_surface_v1 *layer_surface = layer_shell->layer_surface;
if (!layer_surface->output) {
return;
}
struct output *output = output_from_wlr_output(layer_surface->output);
if (!layer_surface->surface->mapped) {
/* is not mapped, usable area will not be changed */
layer_shell_configure_surface(layer_shell, &output->geometry, &output->usable_area);
return;
}
uint32_t committed = layer_surface->current.committed;
if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) {
ky_scene_node_reparent(&layer_shell->tree->node,
manager->layers[layer_surface->current.layer].tree);
committed &= ~WLR_LAYER_SURFACE_V1_STATE_LAYER;
}
if (committed & WLR_LAYER_SURFACE_V1_STATE_KEYBOARD_INTERACTIVITY) {
layer_shell_keyboard_interactivity(layer_shell, input_manager_get_default_seat());
committed &= ~WLR_LAYER_SURFACE_V1_STATE_KEYBOARD_INTERACTIVITY;
}
if (committed) {
layer_shell_configure_surface(layer_shell, &output->geometry, &output->usable_area);
output_update_usable_area(&output->base);
}
}
static void layer_shell_handle_map(struct wl_listener *listener, void *data)
{
struct layer_shell *layer_shell = wl_container_of(listener, layer_shell, map);
struct wlr_layer_surface_v1 *layer_surface = layer_shell->layer_surface;
/* layer-shell first configure is done in commit */
ky_scene_node_set_enabled(&layer_shell->tree->node, true);
if (layer_surface->current.exclusive_zone > 0) {
struct output *output = output_from_wlr_output(layer_surface->output);
output_update_usable_area(&output->base);
}
layer_shell_keyboard_interactivity(layer_shell, input_manager_get_default_seat());
}
/* called when precommit */
static void layer_shell_handle_unmap(struct wl_listener *listener, void *data)
{
struct layer_shell *layer_shell = wl_container_of(listener, layer_shell, unmap);
struct wlr_layer_surface_v1 *layer_surface = layer_shell->layer_surface;
ky_scene_node_set_enabled(&layer_shell->tree->node, false);
layer_shell_keyboard_interactivity(layer_shell, input_manager_get_default_seat());
if (layer_surface->output && layer_surface->current.exclusive_zone > 0) {
output_update_usable_area(&output_from_wlr_output(layer_surface->output)->base);
}
}
static void layer_shell_handle_destroy(struct wl_listener *listener, void *data)
{
struct layer_shell *layer_shell = wl_container_of(listener, layer_shell, destroy);
wl_list_remove(&layer_shell->destroy.link);
wl_list_remove(&layer_shell->commit.link);
wl_list_remove(&layer_shell->map.link);
wl_list_remove(&layer_shell->unmap.link);
wl_list_remove(&layer_shell->new_popup.link);
wl_list_remove(&layer_shell->link);
ky_scene_node_destroy(&layer_shell->tree->node);
free(layer_shell);
}
static void layer_shell_handle_new_popup(struct wl_listener *listener, void *data)
{
struct layer_shell *layer_shell = wl_container_of(listener, layer_shell, new_popup);
struct wlr_xdg_popup *wlr_xdg_popup = data;
struct view_layer *popup_layer = view_manager_get_layer(LAYER_POPUP, false);
xdg_popup_create(wlr_xdg_popup, layer_shell->tree, popup_layer, false);
}
static bool layer_shell_hover(struct seat *seat, struct ky_scene_node *node, double x, double y,
uint32_t time, bool first, bool hold, void *data)
{
struct wlr_surface *surface = wlr_surface_try_from_node(node);
if (first) {
kywc_log(KYWC_DEBUG, "First hover surface %p (%f %f)", surface, x, y);
}
seat_notify_motion(seat, surface, time, x, y, first);
return false;
}
static void layer_shell_click(struct seat *seat, struct ky_scene_node *node, uint32_t button,
bool pressed, uint32_t time, enum click_state state, void *data)
{
struct layer_shell *layer_shell = data;
seat_notify_button(seat, time, button, pressed);
layer_shell_keyboard_interactivity(layer_shell, seat);
}
static void layer_shell_leave(struct seat *seat, struct ky_scene_node *node, bool last, void *data)
{
/* so surface will call set_cursor when enter again */
struct wlr_surface *surface = wlr_surface_try_from_node(node);
seat_notify_leave(seat, surface);
}
static struct ky_scene_node *layer_shell_get_root(void *data)
{
struct layer_shell *layer_shell = data;
return &layer_shell->tree->node;
}
static struct wlr_surface *layer_shell_get_toplevel(void *data)
{
struct layer_shell *layer_shell = data;
return layer_shell->layer_surface->surface;
}
static const struct input_event_node_impl layer_shell_event_node_impl = {
.hover = layer_shell_hover,
.click = layer_shell_click,
.leave = layer_shell_leave,
};
static void handle_new_layer_surface(struct wl_listener *listener, void *data)
{
struct wlr_layer_surface_v1 *layer_surface = data;
/* the user most recently interacted with. */
if (!layer_surface->output) {
struct output *output = input_current_output(input_manager_get_default_seat());
if (!output) {
kywc_log(KYWC_WARN, "Cannot find output for layer shell");
wlr_layer_surface_v1_destroy(layer_surface);
return;
}
layer_surface->output = output->wlr_output;
}
struct layer_shell *layer_shell = calloc(1, sizeof(*layer_shell));
if (!layer_shell) {
return;
}
struct layer_output *layer_output = layer_output_from_wlr_output(layer_surface->output);
wl_list_insert(&layer_output->shells[layer_surface->current.layer], &layer_shell->link);
layer_shell->layer_surface = layer_surface;
layer_shell->tree = ky_scene_tree_create(manager->layers[layer_surface->current.layer].tree);
ky_scene_subsurface_tree_create(layer_shell->tree, layer_surface->surface);
input_event_node_create(&layer_shell->tree->node, &layer_shell_event_node_impl,
layer_shell_get_root, layer_shell_get_toplevel, layer_shell);
layer_shell->commit.notify = layer_shell_handle_commit;
wl_signal_add(&layer_surface->surface->events.commit, &layer_shell->commit);
layer_shell->map.notify = layer_shell_handle_map;
wl_signal_add(&layer_surface->surface->events.map, &layer_shell->map);
layer_shell->unmap.notify = layer_shell_handle_unmap;
wl_signal_add(&layer_surface->surface->events.unmap, &layer_shell->unmap);
layer_shell->destroy.notify = layer_shell_handle_destroy;
wl_signal_add(&layer_surface->events.destroy, &layer_shell->destroy);
layer_shell->new_popup.notify = layer_shell_handle_new_popup;
wl_signal_add(&layer_surface->events.new_popup, &layer_shell->new_popup);
ky_scene_node_set_enabled(&layer_shell->tree->node, layer_surface->surface->mapped);
}
static void layer_output_destroy_shells(struct layer_output *layer_output)
{
struct layer_shell *layer_shell, *tmp;
for (int i = 0; i < 4; i++) {
wl_list_for_each_safe(layer_shell, tmp, &layer_output->shells[i], link) {
wl_list_remove(&layer_shell->link);
wl_list_init(&layer_shell->link);
/* mark output is null, skip update output when unmap */
layer_shell->layer_surface->output = NULL;
wlr_layer_surface_v1_destroy(layer_shell->layer_surface);
}
}
}
static void handle_output_off(struct wl_listener *listener, void *data)
{
struct layer_output *layer_output = wl_container_of(listener, layer_output, off);
layer_output_destroy_shells(layer_output);
}
static void handle_output_destroy(struct wl_listener *listener, void *data)
{
struct layer_output *layer_output = wl_container_of(listener, layer_output, destroy);
/* output is enabled when destroy */
if (layer_output->output->base.state.enabled) {
layer_output_destroy_shells(layer_output);
}
wl_list_remove(&layer_output->destroy.link);
wl_list_remove(&layer_output->geometry.link);
wl_list_remove(&layer_output->usable_area.link);
wl_list_remove(&layer_output->update_usable_area.link);
wl_list_remove(&layer_output->off.link);
wl_list_remove(&layer_output->link);
free(layer_output);
}
static void handle_output_geometry(struct wl_listener *listener, void *data)
{
struct layer_output *layer_output = wl_container_of(listener, layer_output, geometry);
/* configure layer-shells that exclusive_zone < 0 */
struct layer_shell *layer_shell;
for (int i = 0; i < 4; i++) {
wl_list_for_each(layer_shell, &layer_output->shells[i], link) {
if (layer_shell->layer_surface->current.exclusive_zone >= 0) {
continue;
}
layer_shell_configure_surface(layer_shell, &layer_output->output->geometry,
&layer_output->output->usable_area);
}
}
}
static void handle_output_usable_area(struct wl_listener *listener, void *data)
{
struct layer_output *layer_output = wl_container_of(listener, layer_output, usable_area);
/* configure layer-shells that exclusive_zone == 0 */
struct layer_shell *layer_shell;
for (int i = 0; i < 4; i++) {
wl_list_for_each(layer_shell, &layer_output->shells[i], link) {
if (layer_shell->layer_surface->current.exclusive_zone != 0) {
continue;
}
layer_shell_configure_surface(layer_shell, &layer_output->output->geometry,
&layer_output->output->usable_area);
}
}
}
static void handle_output_update_usable_area(struct wl_listener *listener, void *data)
{
struct layer_output *layer_output = wl_container_of(listener, layer_output, update_usable_area);
struct kywc_box *usable_area = data;
/* update usable area and configure layer-shells that exclusive_zone > 0 */
struct layer_shell *layer_shell;
for (int i = 0; i < 4; i++) {
wl_list_for_each(layer_shell, &layer_output->shells[i], link) {
if (layer_shell->layer_surface->current.exclusive_zone <= 0) {
continue;
}
layer_shell_configure_surface(layer_shell, &layer_output->output->geometry,
usable_area);
}
}
}
static void handle_new_output(struct wl_listener *listener, void *data)
{
struct layer_output *layer_output = calloc(1, sizeof(*layer_output));
if (!layer_output) {
return;
}
struct output *output = output_from_kywc_output(data);
for (int i = 0; i < 4; i++) {
wl_list_init(&layer_output->shells[i]);
}
wl_list_insert(&manager->outputs, &layer_output->link);
layer_output->output = output;
layer_output->off.notify = handle_output_off;
wl_signal_add(&output->base.events.off, &layer_output->off);
layer_output->destroy.notify = handle_output_destroy;
wl_signal_add(&output->base.events.destroy, &layer_output->destroy);
layer_output->geometry.notify = handle_output_geometry;
wl_signal_add(&output->events.geometry, &layer_output->geometry);
layer_output->usable_area.notify = handle_output_usable_area;
wl_signal_add(&output->events.usable_area, &layer_output->usable_area);
layer_output->update_usable_area.notify = handle_output_update_usable_area;
output_add_update_usable_area_listener(&output->base, &layer_output->update_usable_area, true);
}
static void handle_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->destroy.link);
wl_list_remove(&manager->new_output.link);
wl_list_remove(&manager->new_surface.link);
free(manager);
manager = NULL;
}
bool wlr_layer_shell_manager_create(struct server *server)
{
manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
struct wlr_layer_shell_v1 *wlr_layer_shell = wlr_layer_shell_v1_create(server->display, 4);
if (!wlr_layer_shell) {
kywc_log(KYWC_WARN, "Wlroots layer shell create failed");
free(manager);
return false;
}
wl_list_init(&manager->outputs);
/* create bottom and top layer tree not in workspace */
manager->layers[0] = *view_manager_get_layer(LAYER_DESKTOP, false);
manager->layers[1] = *view_manager_get_layer(LAYER_BELOW, false);
manager->layers[1].tree = ky_scene_tree_create(manager->layers[1].tree);
manager->layers[2] = *view_manager_get_layer(LAYER_ABOVE, false);
manager->layers[2].tree = ky_scene_tree_create(manager->layers[2].tree);
manager->layers[3] = *view_manager_get_layer(LAYER_UNMANAGED, false);
manager->destroy.notify = handle_destroy;
wl_signal_add(&wlr_layer_shell->events.destroy, &manager->destroy);
manager->new_surface.notify = handle_new_layer_surface;
wl_signal_add(&wlr_layer_shell->events.new_surface, &manager->new_surface);
manager->new_output.notify = handle_new_output;
kywc_output_add_new_listener(&manager->new_output);
return true;
}
kylin-wayland-compositor/src/view/ky_workspace.c 0000664 0001750 0001750 00000030106 15160460057 021071 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include "kywc-workspace-v1-protocol.h"
#include "view/workspace.h"
#include "view_p.h"
struct ky_workspace_manager {
struct wl_event_loop *event_loop;
struct wl_global *global;
struct wl_list resources;
struct wl_list workspaces;
struct wl_event_source *idle_source;
struct wl_listener new_workspace;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct ky_workspace {
struct ky_workspace_manager *manager;
struct wl_list link;
struct wl_list resources;
struct workspace *workspace;
struct wl_listener name;
struct wl_listener position;
struct wl_listener activate;
struct wl_listener destroy;
};
static void workspace_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void workspace_handle_set_position(struct wl_client *client, struct wl_resource *resource,
uint32_t position)
{
struct ky_workspace *ky_workspace = wl_resource_get_user_data(resource);
if (!ky_workspace) {
return;
}
workspace_set_position(ky_workspace->workspace, position);
}
static void workspace_handle_activate(struct wl_client *client, struct wl_resource *resource)
{
struct ky_workspace *ky_workspace = wl_resource_get_user_data(resource);
if (!ky_workspace) {
return;
}
workspace_activate(ky_workspace->workspace);
}
static void workspace_handle_remove(struct wl_client *client, struct wl_resource *resource)
{
struct ky_workspace *ky_workspace = wl_resource_get_user_data(resource);
if (!ky_workspace) {
return;
}
if (wl_list_length(&ky_workspace->manager->workspaces) == 1) {
kywc_log(KYWC_WARN, "Reject to destroy the last workspace");
return;
}
workspace_destroy(ky_workspace->workspace);
}
static void workspace_handle_set_name(struct wl_client *client, struct wl_resource *resource,
const char *name)
{
struct ky_workspace *ky_workspace = wl_resource_get_user_data(resource);
if (!ky_workspace) {
return;
}
workspace_update_name(ky_workspace->workspace, name);
}
static const struct kywc_workspace_v1_interface ky_workspace_impl = {
.destroy = workspace_handle_destroy,
.set_position = workspace_handle_set_position,
.activate = workspace_handle_activate,
.remove = workspace_handle_remove,
.set_name = workspace_handle_set_name,
};
static void ky_workspace_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
}
static struct wl_resource *
create_workspace_resource_for_resource(struct ky_workspace *ky_workspace,
struct wl_resource *manager_resource)
{
struct wl_client *client = wl_resource_get_client(manager_resource);
struct wl_resource *resource = wl_resource_create(client, &kywc_workspace_v1_interface,
wl_resource_get_version(manager_resource), 0);
if (!resource) {
wl_client_post_no_memory(client);
return NULL;
}
wl_resource_set_implementation(resource, &ky_workspace_impl, ky_workspace,
ky_workspace_resource_destroy);
wl_list_insert(&ky_workspace->resources, wl_resource_get_link(resource));
kywc_workspace_manager_v1_send_workspace(manager_resource, resource,
ky_workspace->workspace->uuid);
return resource;
}
static void workspace_send_details_to_workspace_resource(struct ky_workspace *ky_workspace,
struct wl_resource *resource)
{
struct workspace *workspace = ky_workspace->workspace;
kywc_workspace_v1_send_name(resource, workspace->name);
kywc_workspace_v1_send_position(resource, workspace->position);
if (workspace->activated) {
kywc_workspace_v1_send_activated(resource);
} else {
kywc_workspace_v1_send_deactivated(resource);
}
}
static void manager_handle_create_workspace(struct wl_client *client, struct wl_resource *resource,
const char *name, uint32_t position)
{
workspace_create(name, position);
}
static void manager_handle_stop(struct wl_client *client, struct wl_resource *resource)
{
kywc_workspace_manager_v1_send_finished(resource);
wl_resource_destroy(resource);
}
static const struct kywc_workspace_manager_v1_interface ky_workspace_manager_impl = {
.create_workspace = manager_handle_create_workspace,
.stop = manager_handle_stop,
};
static void ky_workspace_manager_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
}
static struct ky_workspace *get_ky_workspace(struct ky_workspace_manager *manager,
struct workspace *workspace)
{
struct ky_workspace *ky_workspace;
wl_list_for_each(ky_workspace, &manager->workspaces, link) {
if (ky_workspace->workspace == workspace) {
return ky_workspace;
}
}
return NULL;
}
static void ky_workspace_manager_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct ky_workspace_manager *manager = data;
struct wl_resource *resource =
wl_resource_create(client, &kywc_workspace_manager_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &ky_workspace_manager_impl, manager,
ky_workspace_manager_resource_destroy);
wl_list_insert(&manager->resources, wl_resource_get_link(resource));
/* send all workspaces and details */
struct ky_workspace *ky_workspace;
for (uint32_t i = 0; i < workspace_manager_get_count(); i++) {
ky_workspace = get_ky_workspace(manager, workspace_by_position(i));
struct wl_resource *workspace_resource =
create_workspace_resource_for_resource(ky_workspace, resource);
workspace_send_details_to_workspace_resource(ky_workspace, workspace_resource);
}
kywc_workspace_manager_v1_send_done(resource);
}
static void manager_idle_send_done(void *data)
{
struct ky_workspace_manager *manager = data;
struct wl_resource *resource;
wl_resource_for_each(resource, &manager->resources) {
kywc_workspace_manager_v1_send_done(resource);
}
manager->idle_source = NULL;
}
static void manager_update_idle_source(struct ky_workspace_manager *manager)
{
if (manager->idle_source || wl_list_empty(&manager->resources)) {
return;
}
manager->idle_source =
wl_event_loop_add_idle(manager->event_loop, manager_idle_send_done, manager);
}
static void handle_workspace_destroy(struct wl_listener *listener, void *data)
{
struct ky_workspace *ky_workspace = wl_container_of(listener, ky_workspace, destroy);
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &ky_workspace->resources) {
kywc_workspace_v1_send_removed(resource);
// make the resource inert
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
wl_resource_set_user_data(resource, NULL);
}
wl_list_remove(&ky_workspace->name.link);
wl_list_remove(&ky_workspace->position.link);
wl_list_remove(&ky_workspace->activate.link);
wl_list_remove(&ky_workspace->destroy.link);
wl_list_remove(&ky_workspace->link);
free(ky_workspace);
}
static void handle_workspace_activate(struct wl_listener *listener, void *data)
{
struct ky_workspace *ky_workspace = wl_container_of(listener, ky_workspace, activate);
struct wl_resource *resource;
wl_resource_for_each(resource, &ky_workspace->resources) {
if (ky_workspace->workspace->activated) {
kywc_workspace_v1_send_activated(resource);
} else {
kywc_workspace_v1_send_deactivated(resource);
}
}
manager_update_idle_source(ky_workspace->manager);
}
static void handle_workspace_name(struct wl_listener *listener, void *data)
{
struct ky_workspace *ky_workspace = wl_container_of(listener, ky_workspace, name);
struct wl_resource *resource;
wl_resource_for_each(resource, &ky_workspace->resources) {
kywc_workspace_v1_send_name(resource, ky_workspace->workspace->name);
}
manager_update_idle_source(ky_workspace->manager);
}
static void handle_workspace_position(struct wl_listener *listener, void *data)
{
struct ky_workspace *ky_workspace = wl_container_of(listener, ky_workspace, position);
struct wl_resource *resource;
wl_resource_for_each(resource, &ky_workspace->resources) {
kywc_workspace_v1_send_position(resource, ky_workspace->workspace->position);
}
manager_update_idle_source(ky_workspace->manager);
}
static void handle_new_workspace(struct wl_listener *listener, void *data)
{
struct ky_workspace *ky_workspace = calloc(1, sizeof(*ky_workspace));
if (!ky_workspace) {
return;
}
struct ky_workspace_manager *manager = wl_container_of(listener, manager, new_workspace);
ky_workspace->manager = manager;
wl_list_init(&ky_workspace->resources);
wl_list_insert(&manager->workspaces, &ky_workspace->link);
struct workspace *workspace = data;
ky_workspace->workspace = workspace;
ky_workspace->name.notify = handle_workspace_name;
wl_signal_add(&workspace->events.name, &ky_workspace->name);
ky_workspace->position.notify = handle_workspace_position;
wl_signal_add(&workspace->events.position, &ky_workspace->position);
ky_workspace->activate.notify = handle_workspace_activate;
wl_signal_add(&workspace->events.activate, &ky_workspace->activate);
ky_workspace->destroy.notify = handle_workspace_destroy;
wl_signal_add(&workspace->events.destroy, &ky_workspace->destroy);
/* send workspaces and detail */
struct wl_resource *resource, *workspace_resource;
wl_resource_for_each(resource, &manager->resources) {
workspace_resource = create_workspace_resource_for_resource(ky_workspace, resource);
workspace_send_details_to_workspace_resource(ky_workspace, workspace_resource);
kywc_workspace_manager_v1_send_done(resource);
}
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct ky_workspace_manager *manager = wl_container_of(listener, manager, server_destroy);
wl_list_remove(&manager->server_destroy.link);
free(manager);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
struct ky_workspace_manager *manager = wl_container_of(listener, manager, display_destroy);
wl_list_remove(&manager->new_workspace.link);
wl_list_remove(&manager->display_destroy.link);
if (manager->idle_source) {
wl_event_source_remove(manager->idle_source);
}
wl_global_destroy(manager->global);
}
bool ky_workspace_manager_create(struct server *server)
{
struct ky_workspace_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->global = wl_global_create(server->display, &kywc_workspace_manager_v1_interface, 1,
manager, ky_workspace_manager_bind);
if (!manager->global) {
kywc_log(KYWC_WARN, "KYWC workspace manager create failed");
free(manager);
return false;
}
wl_list_init(&manager->resources);
wl_list_init(&manager->workspaces);
manager->event_loop = wl_display_get_event_loop(server->display);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &manager->server_destroy);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &manager->display_destroy);
manager->new_workspace.notify = handle_new_workspace;
workspace_manager_add_new_listener(&manager->new_workspace);
return true;
}
kylin-wayland-compositor/src/view/exclusive.c 0000664 0001750 0001750 00000021210 15160460057 020373 0 ustar feng feng // SPDX-FileCopyrightText: 2025 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include "output.h"
#include "scene/surface.h"
#include "view/view.h"
struct exclusive_zone_output {
struct wl_list link;
struct exclusive_zone *zone;
struct kywc_output *output;
struct wl_listener update_usable_area;
};
struct exclusive_zone {
struct wl_list outputs;
struct wlr_surface *surface;
struct wl_listener output_enter;
struct wl_listener output_leave;
struct view *view;
struct wl_listener view_unmap;
struct wl_listener view_minimize;
struct wl_listener view_size;
struct wl_listener view_position;
enum kywc_edges edge;
bool late_update, enabled;
};
static void exclusive_zone_update_edge(struct exclusive_zone *zone)
{
struct view *view = zone->view;
struct kywc_output *output = view->output;
struct kywc_box geo;
kywc_output_effective_geometry(output, &geo);
struct kywc_box *view_geo = &view->base.geometry;
int mid_x = view_geo->x + view_geo->width / 2;
int mid_y = view_geo->y + view_geo->height / 2;
if (view_geo->width > view_geo->height) {
if (mid_y - geo.y < geo.y + geo.height - mid_y) {
zone->edge = KYWC_EDGE_TOP;
} else {
zone->edge = KYWC_EDGE_BOTTOM;
}
} else {
if (mid_x - geo.x < geo.x + geo.width - mid_x) {
zone->edge = KYWC_EDGE_LEFT;
} else {
zone->edge = KYWC_EDGE_RIGHT;
}
}
}
static void exclusive_zone_output_destroy(struct exclusive_zone_output *output)
{
wl_list_remove(&output->link);
wl_list_remove(&output->update_usable_area.link);
output_update_usable_area(output->output);
free(output);
}
static void handle_output_update_usable_area(struct wl_listener *listener, void *data)
{
struct exclusive_zone_output *output = wl_container_of(listener, output, update_usable_area);
struct exclusive_zone *zone = output->zone;
struct kywc_box *usable_area = data;
if (zone->view->impl->update_usable_area) {
zone->view->impl->update_usable_area(zone->view, output->output, usable_area, zone->edge);
}
}
static struct exclusive_zone_output *exclusive_zone_output_create(struct exclusive_zone *zone,
struct kywc_output *kywc_output)
{
struct exclusive_zone_output *output = calloc(1, sizeof(*output));
if (!output) {
return NULL;
}
output->zone = zone;
wl_list_insert(&zone->outputs, &output->link);
output->output = kywc_output;
output->update_usable_area.notify = handle_output_update_usable_area;
output_add_update_usable_area_listener(kywc_output, &output->update_usable_area,
zone->late_update);
output_update_usable_area(output->output);
return output;
}
static struct exclusive_zone_output *exclusive_zone_get_output(struct exclusive_zone *zone,
struct kywc_output *kywc_output)
{
struct exclusive_zone_output *output;
wl_list_for_each(output, &zone->outputs, link) {
if (output->output == kywc_output) {
return output;
}
}
return NULL;
}
static void handle_output_enter(struct wl_listener *listener, void *data)
{
struct exclusive_zone *zone = wl_container_of(listener, zone, output_enter);
struct ky_scene_output *scene_output = data;
struct kywc_output *kywc_output = &output_from_wlr_output(scene_output->output)->base;
if (exclusive_zone_get_output(zone, kywc_output)) {
kywc_log(KYWC_ERROR, "%s enter output multiple times", zone->view->base.app_id);
return;
}
assert(zone->view->base.mapped);
exclusive_zone_output_create(zone, kywc_output);
}
static void handle_output_leave(struct wl_listener *listener, void *data)
{
struct exclusive_zone *zone = wl_container_of(listener, zone, output_leave);
struct ky_scene_output *scene_output = data;
struct kywc_output *kywc_output = &output_from_wlr_output(scene_output->output)->base;
struct exclusive_zone_output *output = exclusive_zone_get_output(zone, kywc_output);
if (!output) {
kywc_log(KYWC_ERROR, "%s leave an output nerver entered", zone->view->base.app_id);
return;
}
exclusive_zone_output_destroy(output);
}
static void exclusive_zone_set_enabled(struct exclusive_zone *zone, bool enabled)
{
if (zone->enabled == enabled) {
return;
}
zone->enabled = enabled;
if (enabled) {
struct ky_scene_buffer *buffer = ky_scene_buffer_try_from_surface(zone->view->surface);
assert(buffer);
wl_signal_add(&buffer->events.output_enter, &zone->output_enter);
wl_signal_add(&buffer->events.output_leave, &zone->output_leave);
wl_signal_add(&zone->view->base.events.size, &zone->view_size);
wl_signal_add(&zone->view->base.events.position, &zone->view_position);
exclusive_zone_update_edge(zone);
// add all outputs to zone
struct wlr_surface_output *surface_output;
wl_list_for_each(surface_output, &zone->surface->current_outputs, link) {
exclusive_zone_output_create(zone,
&output_from_wlr_output(surface_output->output)->base);
}
return;
}
wl_list_remove(&zone->output_enter.link);
wl_list_init(&zone->output_enter.link);
wl_list_remove(&zone->output_leave.link);
wl_list_init(&zone->output_leave.link);
wl_list_remove(&zone->view_size.link);
wl_list_init(&zone->view_size.link);
wl_list_remove(&zone->view_position.link);
wl_list_init(&zone->view_position.link);
struct exclusive_zone_output *output, *tmp;
wl_list_for_each_safe(output, tmp, &zone->outputs, link) {
exclusive_zone_output_destroy(output);
}
}
static void exclusive_zone_destroy(struct exclusive_zone *zone)
{
wl_list_remove(&zone->view_unmap.link);
wl_list_remove(&zone->view_minimize.link);
exclusive_zone_set_enabled(zone, false);
free(zone);
}
static void handle_view_unmap(struct wl_listener *listener, void *data)
{
struct exclusive_zone *zone = wl_container_of(listener, zone, view_unmap);
zone->view->exclusive_zone = NULL;
exclusive_zone_destroy(zone);
}
static void exclusive_zone_update(struct exclusive_zone *zone)
{
exclusive_zone_update_edge(zone);
struct exclusive_zone_output *output;
wl_list_for_each(output, &zone->outputs, link) {
output_update_usable_area(output->output);
}
}
static void handle_view_size(struct wl_listener *listener, void *data)
{
struct exclusive_zone *zone = wl_container_of(listener, zone, view_size);
exclusive_zone_update(zone);
}
static void handle_view_position(struct wl_listener *listener, void *data)
{
struct exclusive_zone *zone = wl_container_of(listener, zone, view_position);
exclusive_zone_update(zone);
}
static void handle_view_minimize(struct wl_listener *listener, void *data)
{
struct exclusive_zone *zone = wl_container_of(listener, zone, view_minimize);
struct kywc_view *view = &zone->view->base;
exclusive_zone_set_enabled(zone, !view->minimized);
}
static struct exclusive_zone *exclusive_zone_create(struct view *view)
{
if (!view->base.mapped) {
kywc_log(KYWC_ERROR, "add unmapped view(%s) to exclusive zone", view->base.app_id);
return NULL;
}
struct exclusive_zone *zone = calloc(1, sizeof(*zone));
if (!zone) {
return NULL;
}
zone->late_update = false;
zone->surface = view->surface;
wl_list_init(&zone->outputs);
zone->output_enter.notify = handle_output_enter;
wl_list_init(&zone->output_enter.link);
zone->output_leave.notify = handle_output_leave;
wl_list_init(&zone->output_leave.link);
zone->view = view;
zone->view_unmap.notify = handle_view_unmap;
wl_signal_add(&view->base.events.unmap, &zone->view_unmap);
zone->view_minimize.notify = handle_view_minimize;
wl_signal_add(&view->base.events.minimize, &zone->view_minimize);
zone->view_size.notify = handle_view_size;
zone->view_position.notify = handle_view_position;
wl_list_init(&zone->view_size.link);
wl_list_init(&zone->view_position.link);
handle_view_minimize(&zone->view_minimize, NULL);
return zone;
}
void view_set_exclusive(struct view *view, bool exclusive)
{
if (exclusive == !!view->exclusive_zone) {
return;
}
if (exclusive) {
view->exclusive_zone = exclusive_zone_create(view);
} else {
exclusive_zone_destroy(view->exclusive_zone);
view->exclusive_zone = NULL;
}
}
kylin-wayland-compositor/src/view/meson.build 0000664 0001750 0001750 00000002714 15160461067 020374 0 ustar feng feng wlcom_sources += files(
'action.c',
'config.c',
'decoration.c',
'exclusive.c',
'global_authentication.c',
'interactive.c',
'maximize_switcher.c',
'modal.c',
'positioner.c',
'ssd.c',
'stack_mode.c',
'tablet_mode.c',
'tile_flyout.c',
'view.c',
'window_menu.c',
'workspace.c',
'xdg_dialog.c',
'xdg_popup.c',
'xdg_shell.c',
'xdg_activation.c',
)
wlcom_sources += files(
'ky_toplevel.c',
'ky_workspace.c',
)
if have_kde_virtual_desktop
wlcom_sources += files(
'kde_virtual_desktop.c',
)
endif
if have_wlr_foreign_toplevel
wlcom_sources += files(
'wlr_foreign_toplevel.c',
)
endif
if have_wlr_layer_shell
wlcom_sources += files(
'wlr_layer_shell.c',
)
endif
if have_kde_plasma_shell
wlcom_sources += files(
'kde_plasma_shell.c',
)
endif
if have_kde_plasma_window_management
wlcom_sources += files(
'kde_plasma_window.c',
)
endif
if have_kde_blur
wlcom_sources += files(
'kde_blur.c',
)
endif
if have_kde_slide
wlcom_sources += files(
'kde_slide.c',
)
endif
if have_ukui_shell
wlcom_sources += files(
'ukui_shell.c',
)
endif
if have_ukui_window_management
wlcom_sources += files(
'ukui_window.c',
)
endif
if have_ukui_blur
wlcom_sources += files(
'ukui_blur.c',
)
endif
if have_ukui_startup
wlcom_sources += files(
'ukui_startup.c',
)
endif
if have_xdg_toplevel_icon
wlcom_sources += files(
'xdg_toplevel_icon.c',
)
endif
subdir('tile')
kylin-wayland-compositor/src/view/ssd.c 0000664 0001750 0001750 00000127500 15160461067 017170 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include
#include
#include "effect/fade.h"
#include "input/cursor.h"
#include "nls.h"
#include "output.h"
#include "painter.h"
#include "scene/decoration.h"
#include "theme.h"
#include "util/color.h"
#include "util/macros.h"
#include "view/action.h"
#include "view_p.h"
#include "widget/scaled_buffer.h"
#include "widget/widget.h"
#define RESIZE_BORDER (13)
enum {
/* buttons */
SSD_BUTTON_MINIMIZE = 0,
SSD_BUTTON_MAXIMIZE,
SSD_BUTTON_CLOSE,
/* titlebar */
SSD_TITLE_ICON,
SSD_TITLE_TEXT,
/* title_rect, border, extend */
SSD_FRAME_RECT,
SSD_PART_COUNT,
};
enum button_state {
BUTTON_STATE_NONE = 0,
BUTTON_STATE_HOVER,
BUTTON_STATE_CLICKED,
};
enum button_mask {
BUTTON_MASK_NONE = 0,
BUTTON_MASK_MINIMIZE = 1 << 0,
BUTTON_MASK_MAXIMIZE = 1 << 1,
BUTTON_MASK_CLOSE = 1 << 2,
BUTTON_MASK_ALL = (1 << 3) - 1,
};
enum ssd_update_cause {
SSD_UPDATE_CAUSE_NONE = 0,
SSD_UPDATE_CAUSE_SIZE = 1 << 0,
SSD_UPDATE_CAUSE_MAXIMIZE = 1 << 1,
SSD_UPDATE_CAUSE_TILE = 1 << 2,
SSD_UPDATE_CAUSE_TITLE = 1 << 3,
SSD_UPDATE_CAUSE_ACTIVATE = 1 << 4,
SSD_UPDATE_CAUSE_FULLSCREEN = 1 << 5,
SSD_UPDATE_CAUSE_CREATE = 1 << 6,
SSD_UPDATE_CAUSE_ALL = (1 << 7) - 1,
};
struct ssd_tooltip {
struct wl_list link;
struct ky_scene_tree *tree;
struct ky_scene_decoration *deco;
struct widget *minimize, *maximize, *restore, *close;
struct wl_listener theme_update;
struct seat *seat;
struct wl_listener seat_destroy;
struct ssd_part *hovered_part;
struct wl_listener hovered_view_unmap;
struct wl_event_source *timer;
bool timer_triggered, timer_for_hidden;
};
struct ssd_manager {
/* enable or disable all ssds */
struct wl_list ssds;
struct wl_list tooltips;
struct wl_listener new_view;
struct wl_listener server_destroy;
};
struct ssd_part {
int type;
struct ssd *ssd;
struct ky_scene_node *node;
float scale;
};
struct ssd {
struct wl_list link;
struct kywc_view *kywc_view;
struct wl_listener view_map;
struct wl_listener view_unmap;
struct wl_listener view_destroy;
struct wl_listener view_decoration;
struct wl_listener view_activate;
struct wl_listener view_size;
struct wl_listener view_tile;
struct wl_listener view_title;
struct wl_listener view_maximize;
struct wl_listener view_fullscreen;
struct wl_listener view_capabilities;
struct wl_listener view_icon_update;
struct wl_listener theme_update;
struct ky_scene_tree *tree;
struct ky_scene_tree *button_tree;
struct ky_scene_tree *titlebar_tree;
struct ssd_part parts[SSD_PART_COUNT];
struct widget *title_text;
bool created;
/* view size to reduce redraw */
int view_width, view_height;
uint32_t buttons;
int button_count;
};
static struct ssd_manager *manager = NULL;
static const char *ssd_part_name[SSD_PART_COUNT] = {
"button_minimize", "button_maximize", "button_close", "title_icon", "title_text", "frame_rect",
};
/**
* button tooltip support
*/
static struct ssd_tooltip *ssd_tooltip_create(struct seat *seat);
static void ssd_check_buttons(struct ssd *ssd);
static struct ssd_tooltip *ssd_tooltip_from_seat(struct seat *seat)
{
struct ssd_tooltip *tooltip;
wl_list_for_each(tooltip, &manager->tooltips, link) {
if (tooltip->seat == seat) {
return tooltip;
}
}
return ssd_tooltip_create(seat);
}
static void ssd_tooltip_show(struct seat *seat, struct ssd_part *part, bool enabled)
{
struct ssd_tooltip *tooltip = ssd_tooltip_from_seat(seat);
if (!tooltip) {
return;
}
struct theme *theme = theme_manager_get_theme();
struct widget *widget;
switch (part->type) {
case SSD_BUTTON_MINIMIZE:
widget = tooltip->minimize;
break;
case SSD_BUTTON_MAXIMIZE:
widget = part->ssd->kywc_view->maximized ? tooltip->restore : tooltip->maximize;
break;
case SSD_BUTTON_CLOSE:
widget = tooltip->close;
break;
default:
return;
}
if (!enabled) {
wl_event_source_timer_update(tooltip->timer, 0);
tooltip->timer_triggered = false;
tooltip->timer_for_hidden = false;
tooltip->hovered_part = NULL;
wl_list_remove(&tooltip->hovered_view_unmap.link);
wl_list_init(&tooltip->hovered_view_unmap.link);
struct ky_scene_node *node = &tooltip->tree->node;
if (node->enabled) {
popup_add_fade_effect(node, FADE_OUT, true, false, widget_get_scale(widget));
ky_scene_node_set_enabled(node, false);
}
/* make sure restore and maximize widgets both are disabled */
if (part->type == SSD_BUTTON_MAXIMIZE) {
widget_set_enabled(tooltip->restore, false);
widget_update(tooltip->restore, true);
widget_set_enabled(tooltip->maximize, false);
widget_update(tooltip->maximize, true);
} else {
widget_set_enabled(widget, false);
widget_update(widget, true);
}
return;
}
if (!tooltip->timer_triggered) {
wl_event_source_timer_update(tooltip->timer, 500);
tooltip->timer_for_hidden = false;
tooltip->hovered_part = part;
wl_list_remove(&tooltip->hovered_view_unmap.link);
wl_signal_add(&part->ssd->kywc_view->events.unmap, &tooltip->hovered_view_unmap);
return;
}
if (part->type == SSD_BUTTON_MAXIMIZE) {
struct view *view = view_from_kywc_view(part->ssd->kywc_view);
int x, y; // position in view
ky_scene_node_coords(part->node, &x, &y);
x -= part->ssd->kywc_view->geometry.x;
y -= part->ssd->kywc_view->geometry.y;
struct kywc_box box = { x, y, theme->button_width, theme->button_width };
if (view_show_tile_flyout(view, seat, &box)) {
return;
}
}
widget_set_enabled(widget, true);
widget_update(widget, true);
int x = seat->cursor->lx;
int y = seat->cursor->ly + theme->icon_size;
int w, h;
widget_get_size(widget, &w, &h);
struct output *output = input_current_output(seat);
int max_x = output->geometry.x + output->geometry.width;
int max_y = output->geometry.y + output->geometry.height;
if (x + w > max_x) {
x = max_x - w;
}
if (y + h > max_y) {
y = seat->cursor->ly - h;
}
struct ky_scene_node *node = &tooltip->tree->node;
ky_scene_decoration_set_surface_size(tooltip->deco, w, h);
ky_scene_node_set_position(node, x, y);
ky_scene_node_raise_to_top(node);
ky_scene_node_set_enabled(node, true);
popup_add_fade_effect(node, FADE_IN, true, false, widget_get_scale(widget));
tooltip->timer_for_hidden = true;
wl_event_source_timer_update(tooltip->timer, 10000);
}
static void ssd_tooltip_draw_widget(struct widget *widget, const char *text)
{
struct theme *theme = theme_manager_get_theme();
int width = 0, height = 0;
painter_get_text_size(text, theme->font_name, theme->font_size, &width, &height);
widget_set_text(widget, text, TEXT_ALIGN_CENTER, TEXT_ATTR_NONE);
widget_set_font(widget, theme->font_name, theme->font_size);
widget_set_max_size(widget, width * 2, height * 2);
widget_set_auto_resize(widget, AUTO_RESIZE_EXTEND);
widget_set_front_color(widget, theme->active_text_color);
widget_update(widget, true);
}
static void ssd_tooltip_draw_widgets(struct ssd_tooltip *tooltip)
{
struct theme *theme = theme_manager_get_theme();
ky_scene_decoration_set_shadow_count(tooltip->deco, theme->menu_shadow_color.num_layers);
for (int i = 0; i < theme->menu_shadow_color.num_layers; i++) {
struct theme_shadow_layer *shadow = &theme->menu_shadow_color.layers[i];
float shadow_color[4];
color_float_pa(shadow_color, shadow->color);
ky_scene_decoration_set_shadow(tooltip->deco, i, shadow->off_x, shadow->off_y,
shadow->spread, shadow->blur, shadow_color);
}
float border_color[4];
color_float_pa(border_color, theme->active_border_color);
ky_scene_decoration_set_margin_color(tooltip->deco, (float[4]){ 0, 0, 0, 0 }, border_color);
float bg_color[4];
color_float_pax(bg_color, theme->active_bg_color, theme->opacity / 100.0);
ky_scene_decoration_set_surface_color(tooltip->deco, bg_color);
int r = theme->menu_radius;
ky_scene_decoration_set_round_corner_radius(tooltip->deco, (int[4]){ r, r, r, r });
ky_scene_decoration_set_surface_blurred(tooltip->deco, theme->opacity != 100);
ssd_tooltip_draw_widget(tooltip->minimize, tr("Minimize"));
ssd_tooltip_draw_widget(tooltip->maximize, tr("Maximize"));
ssd_tooltip_draw_widget(tooltip->restore, tr("Restore"));
ssd_tooltip_draw_widget(tooltip->close, tr("Close"));
}
static void ssd_tooltip_handle_theme_update(struct wl_listener *listener, void *data)
{
struct ssd_tooltip *tooltip = wl_container_of(listener, tooltip, theme_update);
struct theme_update_event *update_event = data;
uint32_t allowed_mask = THEME_UPDATE_MASK_FONT | THEME_UPDATE_MASK_BACKGROUND_COLOR |
THEME_UPDATE_MASK_CORNER_RADIUS | THEME_UPDATE_MASK_OPACITY |
THEME_UPDATE_MASK_SHADOW_COLOR | THEME_UPDATE_MASK_BORDER_COLOR;
if (update_event->update_mask & allowed_mask) {
ssd_tooltip_draw_widgets(tooltip);
}
}
static int handle_tooltip(void *data)
{
struct ssd_tooltip *tooltip = data;
tooltip->timer_triggered = true;
ssd_tooltip_show(tooltip->seat, tooltip->hovered_part, !tooltip->timer_for_hidden);
return 0;
}
static void ssd_tooltip_handle_seat_destroy(struct wl_listener *listener, void *data)
{
struct ssd_tooltip *tooltip = wl_container_of(listener, tooltip, seat_destroy);
wl_list_remove(&tooltip->seat_destroy.link);
wl_list_remove(&tooltip->theme_update.link);
wl_list_remove(&tooltip->hovered_view_unmap.link);
wl_list_remove(&tooltip->link);
ky_scene_node_destroy(&tooltip->tree->node);
wl_event_source_remove(tooltip->timer);
free(tooltip);
}
static void ssd_tooltip_handle_hoverd_view_unmap(struct wl_listener *listener, void *data)
{
struct ssd_tooltip *tooltip = wl_container_of(listener, tooltip, hovered_view_unmap);
wl_list_remove(&tooltip->hovered_view_unmap.link);
wl_list_init(&tooltip->hovered_view_unmap.link);
if (tooltip->hovered_part) {
ssd_tooltip_show(tooltip->seat, tooltip->hovered_part, false);
}
}
static struct ssd_tooltip *ssd_tooltip_create(struct seat *seat)
{
struct ssd_tooltip *tooltip = calloc(1, sizeof(*tooltip));
if (!tooltip) {
return NULL;
}
tooltip->hovered_view_unmap.notify = ssd_tooltip_handle_hoverd_view_unmap;
wl_list_init(&tooltip->hovered_view_unmap.link);
tooltip->seat = seat;
tooltip->seat_destroy.notify = ssd_tooltip_handle_seat_destroy;
wl_signal_add(&seat->events.destroy, &tooltip->seat_destroy);
wl_list_insert(&manager->tooltips, &tooltip->link);
tooltip->theme_update.notify = ssd_tooltip_handle_theme_update;
theme_manager_add_update_listener(&tooltip->theme_update, false);
struct wl_event_loop *loop = wl_display_get_event_loop(seat->wlr_seat->display);
tooltip->timer = wl_event_loop_add_timer(loop, handle_tooltip, tooltip);
/* create widgets in popup layer */
struct view_layer *layer = view_manager_get_layer(LAYER_POPUP, false);
tooltip->tree = ky_scene_tree_create(layer->tree);
ky_scene_node_set_input_bypassed(&tooltip->tree->node, true);
ky_scene_node_set_enabled(&tooltip->tree->node, false);
tooltip->deco = ky_scene_decoration_create(tooltip->tree);
ky_scene_decoration_set_margin(tooltip->deco, 0, 1);
ky_scene_decoration_set_mask(tooltip->deco, DECORATION_MASK_ALL);
ky_scene_node_set_position(ky_scene_node_from_decoration(tooltip->deco), -1, -1);
tooltip->minimize = widget_create(tooltip->tree);
tooltip->maximize = widget_create(tooltip->tree);
tooltip->restore = widget_create(tooltip->tree);
tooltip->close = widget_create(tooltip->tree);
ssd_tooltip_draw_widgets(tooltip);
return tooltip;
}
static uint32_t get_resize_type(struct ssd_part *part, double x, double y)
{
struct ky_scene_rect *frame = ky_scene_rect_from_node(part->node);
struct theme *theme = theme_manager_get_theme();
int border = part->ssd->kywc_view->ssd & KYWC_SSD_BORDER ? theme->border_width : 0;
int x1 = theme->window_radius + border;
int x2 = frame->width - x1;
int y2 = frame->height - x1;
int sx = floor(x);
int sy = floor(y);
uint32_t resize_edges = KYWC_EDGE_NONE;
if (sx <= x1) {
if (sy <= x1) {
resize_edges = KYWC_EDGE_TOP | KYWC_EDGE_LEFT;
} else if (sy <= y2) {
resize_edges = KYWC_EDGE_LEFT;
} else {
resize_edges = KYWC_EDGE_BOTTOM | KYWC_EDGE_LEFT;
}
} else if (sx >= x2) {
if (sy <= x1) {
resize_edges = KYWC_EDGE_TOP | KYWC_EDGE_RIGHT;
} else if (sy < y2) {
resize_edges = KYWC_EDGE_RIGHT;
} else {
resize_edges = KYWC_EDGE_BOTTOM | KYWC_EDGE_RIGHT;
}
} else if (sy >= y2) {
resize_edges = KYWC_EDGE_BOTTOM;
} else if (sy <= border) {
resize_edges = KYWC_EDGE_TOP;
}
return resize_edges;
}
static void ssd_part_set_button_buffer(struct ssd_part *part, enum button_state state)
{
if (part->type > SSD_BUTTON_CLOSE || (part->ssd->buttons & (1 << part->type)) == 0) {
return;
}
enum theme_button_type type = THEME_BUTTON_TYPE_MINIMIZE;
if (part->type == SSD_BUTTON_MAXIMIZE) {
type = part->ssd->kywc_view->maximized ? THEME_BUTTON_TYPE_RESTORE
: THEME_BUTTON_TYPE_MAXIMIZE;
} else if (part->type == SSD_BUTTON_CLOSE) {
type = THEME_BUTTON_TYPE_CLOSE;
}
struct theme *theme = theme_manager_get_theme();
/* get actual type by current state */
type += state * 4;
struct wlr_fbox src;
struct wlr_buffer *buf =
theme_button_buffer_load(theme, part->scale, type, &src, part->ssd->kywc_view->activated);
struct ky_scene_buffer *buffer = ky_scene_buffer_from_node(part->node);
if (buffer->buffer != buf) {
ky_scene_buffer_set_buffer(buffer, buf);
}
/* shortcut here if set_buffer triggered scaled buffer update */
if (buffer->buffer != buf) {
return;
}
ky_scene_buffer_set_dest_size(buffer, theme->button_width, theme->button_width);
ky_scene_buffer_set_source_box(buffer, &src);
}
static bool ssd_hover(struct seat *seat, struct ky_scene_node *node, double x, double y,
uint32_t time, bool first, bool hold, void *data)
{
if (seat_is_dragging(seat)) {
return false;
}
struct ssd_part *part = data;
/* we actually only need to process when first enter */
if ((!first && part->type != SSD_FRAME_RECT) || hold) {
return false;
}
// kywc_log(KYWC_DEBUG, "ssd hover %s", ssd_part_name[part->type]);
switch (part->type) {
case SSD_BUTTON_MINIMIZE ... SSD_BUTTON_CLOSE:
ssd_part_set_button_buffer(part, BUTTON_STATE_HOVER);
ssd_tooltip_show(seat, part, true);
cursor_set_image(seat->cursor, CURSOR_DEFAULT);
break;
case SSD_FRAME_RECT:
if (!part->ssd->kywc_view->maximized && KYWC_VIEW_IS_RESIZABLE(part->ssd->kywc_view)) {
uint32_t edges = get_resize_type(part, x, y);
cursor_set_resize_image(seat->cursor, edges);
view_show_tile_linkage_bar(view_from_kywc_view(part->ssd->kywc_view), edges);
break;
}
// fallthrough
default:
cursor_set_image(seat->cursor, CURSOR_DEFAULT);
break;
}
return false;
}
static void ssd_leave(struct seat *seat, struct ky_scene_node *node, bool last, void *data)
{
if (seat_is_dragging(seat)) {
return;
}
struct ssd_part *part = data;
// kywc_log(KYWC_ERROR, "ssd leave %s", ssd_part_name[part->type]);
switch (part->type) {
case SSD_BUTTON_MINIMIZE ... SSD_BUTTON_CLOSE:
ssd_part_set_button_buffer(part, BUTTON_STATE_NONE);
ssd_tooltip_show(seat, part, false);
break;
case SSD_FRAME_RECT:
/* we may have changed cursor image when hover */
cursor_set_image(seat->cursor, CURSOR_DEFAULT);
tile_linkage_resize_done(view_from_kywc_view(part->ssd->kywc_view), false);
break;
default:
break;
}
}
static void ssd_click(struct seat *seat, struct ky_scene_node *node, uint32_t button, bool pressed,
uint32_t time, enum click_state state, void *data)
{
struct ssd_part *part = data;
struct kywc_view *kywc_view = part->ssd->kywc_view;
struct view *view = view_from_kywc_view(kywc_view);
enum kywc_edges edges = KYWC_EDGE_NONE;
if (part->type >= SSD_BUTTON_MINIMIZE && part->type <= SSD_BUTTON_CLOSE) {
ssd_tooltip_show(seat, part, false);
}
/* active current view */
kywc_view_activate(kywc_view);
view_set_focus(view, seat);
if (CLICK_STATE_DOUBLE == state) {
if (button != BTN_LEFT) {
return;
}
switch (part->type) {
case SSD_FRAME_RECT:
edges = get_resize_type(part, seat->cursor->sx, seat->cursor->sy);
if (edges != KYWC_EDGE_NONE) {
break;
}
// fallthrough if click in title
case SSD_TITLE_TEXT:
if (KYWC_VIEW_IS_MAXIMIZABLE(kywc_view)) {
kywc_view_toggle_maximized(kywc_view);
}
break;
default:
break;
}
return;
}
if (CLICK_STATE_FOCUS_LOST == state) {
/* menu and ssd buttons do not effective */
return;
}
if (LEFT_BUTTON_PRESSED(button, pressed) && part->type <= SSD_BUTTON_CLOSE) {
ssd_part_set_button_buffer(part, BUTTON_STATE_CLICKED);
}
switch (part->type) {
case SSD_BUTTON_CLOSE:
if (LEFT_BUTTON_RELEASED(button, pressed)) {
kywc_view_close(kywc_view);
}
return;
case SSD_BUTTON_MAXIMIZE:
if (LEFT_BUTTON_RELEASED(button, pressed)) {
kywc_view_toggle_maximized(kywc_view);
break;
}
return;
case SSD_BUTTON_MINIMIZE:
if (LEFT_BUTTON_RELEASED(button, pressed)) {
kywc_view_set_minimized(kywc_view, true);
}
return;
case SSD_TITLE_ICON:
if (LEFT_BUTTON_RELEASED(button, pressed) || RIGHT_BUTTON_RELEASED(button, pressed)) {
view_show_window_menu(view, seat, seat->cursor->lx, seat->cursor->ly);
}
return;
case SSD_FRAME_RECT:
edges = get_resize_type(part, seat->cursor->sx, seat->cursor->sy);
if (edges != KYWC_EDGE_NONE) {
break;
}
// fallthrough if press in title
case SSD_TITLE_TEXT:
if (LEFT_BUTTON_PRESSED(button, pressed)) {
window_begin_move(view, seat);
} else if (RIGHT_BUTTON_PRESSED(button, pressed)) {
/* show window menu, menu will grab seat to hide itself */
view_show_window_menu(view, seat, seat->cursor->lx, seat->cursor->ly);
return;
}
break;
default:
break;
}
if (edges != KYWC_EDGE_NONE && pressed && button == BTN_LEFT) {
window_begin_resize(view, edges, seat);
}
}
static struct ky_scene_node *ssd_get_root(void *data)
{
struct ssd_part *part = data;
return &part->ssd->tree->node;
}
static const struct input_event_node_impl ssd_impl = {
.hover = ssd_hover,
.leave = ssd_leave,
.click = ssd_click,
};
static void ssd_part_set_icon_buffer(struct ssd_part *part)
{
struct kywc_view *kywc_view = part->ssd->kywc_view;
struct view *view = view_from_kywc_view(kywc_view);
struct wlr_buffer *buf = view_get_icon_buffer(view, part->scale);
if (!buf) {
return;
}
struct ky_scene_buffer *buffer = ky_scene_buffer_from_node(part->node);
if (buffer->buffer != buf) {
ky_scene_buffer_set_buffer(buffer, buf);
}
if (buffer->buffer != buf) {
return;
}
struct theme *theme = theme_manager_get_theme();
ky_scene_buffer_set_dest_size(buffer, theme->icon_size, theme->icon_size);
}
static void ssd_update_title_icon(struct ssd *ssd)
{
struct theme *theme = theme_manager_get_theme();
struct view *view = view_from_kywc_view(ssd->kywc_view);
int title_height = view->parent ? theme->subtitle_height : theme->title_height;
int y = theme->border_width + (title_height - theme->icon_size) / 2;
if (theme->layout_is_right_to_left) {
int view_w = ssd->kywc_view->geometry.width + 2 * theme->border_width;
ky_scene_node_set_position(ssd->parts[SSD_TITLE_ICON].node,
view_w - y - theme->button_width, y);
} else {
ky_scene_node_set_position(ssd->parts[SSD_TITLE_ICON].node, y, y);
}
}
static void ssd_update_title_text(struct ssd *ssd, uint32_t cause)
{
struct theme *theme = theme_manager_get_theme();
struct kywc_view *view = ssd->kywc_view;
struct view *tmp = view_from_kywc_view(view);
int title_height = tmp->parent ? theme->subtitle_height : theme->title_height;
int max_width = view->geometry.width - (ssd->button_count + 1.5) * theme->button_width;
/* no space left for title text */
if (max_width <= 0) {
widget_set_enabled(ssd->title_text, false);
widget_update(ssd->title_text, true);
return;
}
/* redraw title buffer */
if (cause & SSD_UPDATE_CAUSE_TITLE) {
widget_set_text(ssd->title_text, view->title, TEXT_ALIGN_LEFT, TEXT_ATTR_NONE);
widget_set_font(ssd->title_text, theme->font_name, theme->font_size);
}
if (cause & SSD_UPDATE_CAUSE_SIZE) {
widget_set_max_size(ssd->title_text, max_width, title_height);
widget_set_auto_resize(ssd->title_text, AUTO_RESIZE_ONLY);
}
if (cause & SSD_UPDATE_CAUSE_ACTIVATE) {
widget_set_front_color(ssd->title_text, view->activated ? theme->active_text_color
: theme->inactive_text_color);
}
widget_set_enabled(ssd->title_text, true);
widget_update(ssd->title_text, true);
/* skip setting position if activate changed only */
if (cause == SSD_UPDATE_CAUSE_ACTIVATE) {
return;
}
/* get actual size when auto-sized */
int text_width, text_height;
widget_get_size(ssd->title_text, &text_width, &text_height);
/* calc the text position by jystify */
int x, y;
y = theme->border_width + (title_height - text_height) / 2;
if (theme->text_justify == JUSTIFY_LEFT) {
x = theme->layout_is_right_to_left ? ssd->button_count * theme->button_width
: theme->button_width;
x += y;
} else if (theme->text_justify == JUSTIFY_CENTER) {
x = (view->geometry.width - text_width) / 2;
/* add a left shift if close to button */
if (text_width + (ssd->button_count + 1) * theme->button_width > max_width) {
x -= theme->button_width;
}
} else {
x = theme->layout_is_right_to_left ? ssd->button_count * theme->button_width
: theme->button_width;
x += max_width - text_width + y;
}
/* setting position directly is better */
ky_scene_node_set_position(ssd->parts[SSD_TITLE_TEXT].node, x, y);
}
static void ssd_update_titlebar(struct ssd *ssd, uint32_t cause)
{
struct theme *theme = theme_manager_get_theme();
struct view *view = view_from_kywc_view(ssd->kywc_view);
int border_w = theme->border_width;
int button_w = theme->button_width;
int title_h = view->parent ? theme->subtitle_height : theme->title_height;
int view_w = ssd->kywc_view->geometry.width;
/* set titlebar subtree position if theme changed */
if (cause & SSD_UPDATE_CAUSE_CREATE) {
ky_scene_node_set_position(&ssd->titlebar_tree->node, -border_w, -(title_h + border_w));
}
/* set button tree position when view w or title height changed */
if (cause & SSD_UPDATE_CAUSE_SIZE) {
int pad = (title_h - button_w) / 2;
int x = theme->layout_is_right_to_left ? pad : (view_w + border_w - 3 * button_w - pad);
int y = pad + border_w;
ky_scene_node_set_position(&ssd->button_tree->node, x, y);
ssd_update_title_icon(ssd);
}
if (cause & SSD_UPDATE_CAUSE_CREATE) {
ky_scene_node_set_enabled(ssd->parts[SSD_BUTTON_MINIMIZE].node,
ssd->buttons & BUTTON_MASK_MINIMIZE);
ky_scene_node_set_enabled(ssd->parts[SSD_BUTTON_MAXIMIZE].node,
ssd->buttons & BUTTON_MASK_MAXIMIZE);
ky_scene_node_set_enabled(ssd->parts[SSD_BUTTON_CLOSE].node,
ssd->buttons & BUTTON_MASK_CLOSE);
ky_scene_node_set_position(ssd->parts[SSD_BUTTON_MAXIMIZE].node, button_w, 0);
if (theme->layout_is_right_to_left) {
ky_scene_node_set_position(
ssd->parts[SSD_BUTTON_MINIMIZE].node,
ssd->buttons & BUTTON_MASK_MAXIMIZE ? 2 * button_w : button_w, 0);
ky_scene_node_set_position(ssd->parts[SSD_BUTTON_CLOSE].node, 0, 0);
} else {
ky_scene_node_set_position(ssd->parts[SSD_BUTTON_MINIMIZE].node,
ssd->buttons & BUTTON_MASK_MAXIMIZE ? 0 : button_w, 0);
ky_scene_node_set_position(ssd->parts[SSD_BUTTON_CLOSE].node, 2 * button_w, 0);
}
ssd_part_set_button_buffer(&ssd->parts[SSD_BUTTON_MINIMIZE], BUTTON_STATE_NONE);
ssd_part_set_button_buffer(&ssd->parts[SSD_BUTTON_CLOSE], BUTTON_STATE_NONE);
}
if (cause & (SSD_UPDATE_CAUSE_TITLE | SSD_UPDATE_CAUSE_ACTIVATE | SSD_UPDATE_CAUSE_SIZE)) {
/* no need to redraw when resize height only */
if (!(cause == SSD_UPDATE_CAUSE_SIZE && ssd->view_width == view_w)) {
ssd_update_title_text(ssd, cause);
}
}
if (cause & SSD_UPDATE_CAUSE_ACTIVATE) {
ssd_part_set_button_buffer(&ssd->parts[SSD_BUTTON_MINIMIZE], BUTTON_STATE_NONE);
ssd_part_set_button_buffer(&ssd->parts[SSD_BUTTON_MAXIMIZE], BUTTON_STATE_NONE);
ssd_part_set_button_buffer(&ssd->parts[SSD_BUTTON_CLOSE], BUTTON_STATE_NONE);
} else if (cause & SSD_UPDATE_CAUSE_MAXIMIZE) {
/* set maximize and restore */
ssd_part_set_button_buffer(&ssd->parts[SSD_BUTTON_MAXIMIZE], BUTTON_STATE_NONE);
}
}
static void ssd_update_frame(struct ssd *ssd, uint32_t cause)
{
struct theme *theme = theme_manager_get_theme();
struct kywc_view *view = ssd->kywc_view;
struct ky_scene_decoration *frame =
ky_scene_decoration_from_node(ssd->parts[SSD_FRAME_RECT].node);
if (cause & SSD_UPDATE_CAUSE_ACTIVATE) {
float border_color[4];
color_float_pa(border_color,
view->activated ? theme->active_border_color : theme->inactive_border_color);
float bg_color[4];
color_float_pa(bg_color,
view->activated ? theme->active_bg_color : theme->inactive_bg_color);
ky_scene_decoration_set_margin_color(frame, bg_color, border_color);
struct theme_shadow *shadow;
if (view->modal) {
shadow = view->activated ? &theme->modal_active_shadow_color
: &theme->modal_inactive_shadow_color;
} else {
shadow = view->activated ? &theme->active_shadow_color : &theme->inactive_shadow_color;
}
ky_scene_decoration_set_shadow_count(frame, shadow->num_layers);
for (int i = 0; i < shadow->num_layers; i++) {
struct theme_shadow_layer *shadow_layer = &shadow->layers[i];
float shadow_color[4];
color_float_pa(shadow_color, shadow_layer->color);
ky_scene_decoration_set_shadow(frame, i, shadow_layer->off_x, shadow_layer->off_y,
shadow_layer->spread, shadow_layer->blur, shadow_color);
}
}
if (cause & (SSD_UPDATE_CAUSE_TILE | SSD_UPDATE_CAUSE_MAXIMIZE)) {
uint32_t deco_mask = DECORATION_MASK_ALL;
if (view->maximized) {
deco_mask = DECORATION_MASK_NONE;
} else if (view->tiled == KYWC_TILE_TOP) {
deco_mask = DECORATION_MASK_BOTTOM;
} else if (view->tiled == KYWC_TILE_BOTTOM) {
deco_mask = DECORATION_MASK_TOP;
} else if (view->tiled == KYWC_TILE_LEFT) {
deco_mask = DECORATION_MASK_RIGHT;
} else if (view->tiled == KYWC_TILE_RIGHT) {
deco_mask = DECORATION_MASK_LEFT;
} else if (view->tiled == KYWC_TILE_TOP_LEFT) {
deco_mask = DECORATION_MASK_RIGHT | DECORATION_MASK_BOTTOM;
} else if (view->tiled == KYWC_TILE_BOTTOM_LEFT) {
deco_mask = DECORATION_MASK_RIGHT | DECORATION_MASK_TOP;
} else if (view->tiled == KYWC_TILE_TOP_RIGHT) {
deco_mask = DECORATION_MASK_LEFT | DECORATION_MASK_BOTTOM;
} else if (view->tiled == KYWC_TILE_BOTTOM_RIGHT) {
deco_mask = DECORATION_MASK_LEFT | DECORATION_MASK_TOP;
}
ky_scene_decoration_set_mask(frame, deco_mask);
}
if (cause & SSD_UPDATE_CAUSE_SIZE) {
ky_scene_decoration_set_surface_size(frame, view->geometry.width, view->geometry.height);
}
if (cause & SSD_UPDATE_CAUSE_CREATE) {
struct view *tmp = view_from_kywc_view(view);
int border = view->ssd & KYWC_SSD_BORDER ? theme->border_width : 0;
int title = view->ssd & KYWC_SSD_TITLE
? tmp->parent ? theme->subtitle_height : theme->title_height
: 0;
int resize = view->ssd & KYWC_SSD_RESIZE ? RESIZE_BORDER : 0;
int bottom = view->has_round_corner ? theme->window_radius : 0;
int top = (view->ssd & KYWC_SSD_TITLE || view->has_round_corner) ? theme->window_radius : 0;
ky_scene_decoration_set_resize_width(frame, resize);
ky_scene_decoration_set_margin(frame, title, border);
ky_scene_decoration_set_round_corner_radius(frame, (int[4]){ bottom, top, bottom, top });
ky_scene_node_set_position(ssd->parts[SSD_FRAME_RECT].node, -border, -border - title);
}
}
static void ssd_update_margin(struct ssd *ssd)
{
struct kywc_view *view = ssd->kywc_view;
struct theme *theme = theme_manager_get_theme();
int title = 0;
if (view->ssd & KYWC_SSD_TITLE) {
if (view_from_kywc_view(view)->parent) {
title = theme->subtitle_height;
} else {
title = theme->title_height;
}
}
view->margin.off_x = 0;
view->margin.off_y = title;
view->margin.off_width = 0;
view->margin.off_height = title;
}
static void ssd_update_padding(struct ssd *ssd)
{
struct kywc_view *view = ssd->kywc_view;
if (view->ssd == KYWC_SSD_NONE) {
view->padding.top = view->padding.bottom = 0;
view->padding.left = view->padding.right = 0;
return;
}
struct theme *theme = theme_manager_get_theme();
struct theme_shadow *shadow;
if (view->modal) {
shadow = view->activated ? &theme->modal_active_shadow_color
: &theme->modal_inactive_shadow_color;
} else {
shadow = view->activated ? &theme->active_shadow_color : &theme->inactive_shadow_color;
}
int border = view->ssd & KYWC_SSD_BORDER ? theme->border_width : 0;
view->padding.top = border;
view->padding.bottom = border;
view->padding.left = border;
view->padding.right = border;
for (int i = 0; i < shadow->num_layers; i++) {
struct theme_shadow_layer *shadow_layer = &shadow->layers[i];
int extend = border + shadow_layer->blur;
view->padding.top = MAX(view->padding.top, extend - shadow_layer->off_y);
view->padding.bottom = MAX(view->padding.bottom, extend + shadow_layer->off_y);
view->padding.left = MAX(view->padding.left, extend - shadow_layer->off_x);
view->padding.right = MAX(view->padding.right, extend + shadow_layer->off_x);
}
}
static void ssd_update_parts(struct ssd *ssd, uint32_t cause)
{
assert(ssd->created && ssd->kywc_view->ssd != KYWC_SSD_NONE);
if (cause & SSD_UPDATE_CAUSE_FULLSCREEN) {
bool enabled = !ssd->kywc_view->fullscreen;
ky_scene_node_set_enabled(&ssd->tree->node, enabled);
}
if (ssd->kywc_view->ssd & KYWC_SSD_TITLE) {
ssd_update_titlebar(ssd, cause);
}
ssd_update_frame(ssd, cause);
}
static void ssd_update_buffer(struct ky_scene_buffer *buffer, float scale, void *data)
{
struct ssd_part *part = data;
part->scale = scale;
/* update scene_buffer with new buffer */
switch (part->type) {
case SSD_BUTTON_MINIMIZE ... SSD_BUTTON_CLOSE:
ssd_part_set_button_buffer(part, BUTTON_STATE_NONE);
break;
case SSD_TITLE_ICON:
ssd_part_set_icon_buffer(part);
break;
}
kywc_log(KYWC_DEBUG, "%s redraw in %f", ssd_part_name[part->type], scale);
}
static void ssd_destroy_buffer(struct ky_scene_buffer *buffer, void *data)
{
struct ssd_part *part = data;
kywc_log(KYWC_DEBUG, "%s node destroy", ssd_part_name[part->type]);
/* buffers are destroyed in theme */
}
static void ssd_create_parts(struct ssd *ssd, float scale)
{
int start = ssd->kywc_view->ssd & KYWC_SSD_TITLE ? 0 : SSD_FRAME_RECT;
/* create buffers from bottom to top */
for (int i = SSD_PART_COUNT - 1; i >= start; i--) {
ssd->parts[i].type = i;
ssd->parts[i].ssd = ssd;
struct ky_scene_tree *parent;
if (i <= SSD_BUTTON_CLOSE) {
parent = ssd->button_tree;
} else if (i <= SSD_TITLE_TEXT) {
parent = ssd->titlebar_tree;
} else {
parent = ssd->tree;
}
if (i < SSD_FRAME_RECT) {
if (i == SSD_TITLE_TEXT) {
ssd->title_text = widget_create(parent);
ssd->parts[i].node = ky_scene_node_from_widget(ssd->title_text);
} else {
struct ky_scene_buffer *buf = scaled_buffer_create(
parent, scale, ssd_update_buffer, ssd_destroy_buffer, &ssd->parts[i]);
ssd->parts[i].node = &buf->node;
ssd->parts[i].scale = scale;
/**
* set_buffer will emit output_enter,
* otherwise we cannot get initial output the view in.
*/
if (i == SSD_TITLE_ICON) {
ssd_part_set_icon_buffer(&ssd->parts[i]);
} else {
ssd_part_set_button_buffer(&ssd->parts[i], BUTTON_STATE_NONE);
}
}
} else {
struct ky_scene_decoration *frame = ky_scene_decoration_create(parent);
ssd->parts[i].node = ky_scene_node_from_decoration(frame);
ky_scene_node_lower_to_bottom(ssd->parts[i].node);
}
input_event_node_create(ssd->parts[i].node, &ssd_impl, ssd_get_root, NULL, &ssd->parts[i]);
}
}
static void handle_theme_update(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, theme_update);
struct theme_update_event *update_event = data;
uint32_t allowed_mask = THEME_UPDATE_MASK_FONT | THEME_UPDATE_MASK_BACKGROUND_COLOR |
THEME_UPDATE_MASK_BORDER_COLOR | THEME_UPDATE_MASK_CORNER_RADIUS |
THEME_UPDATE_MASK_SHADOW_COLOR | THEME_UPDATE_MASK_DECORATION_SIZE;
if (update_event->update_mask & allowed_mask) {
ssd_update_margin(ssd);
ssd_update_padding(ssd);
ssd_check_buttons(ssd);
ssd_update_parts(ssd, SSD_UPDATE_CAUSE_ALL);
}
}
static void handle_view_icon_update(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_icon_update);
ssd_part_set_icon_buffer(&ssd->parts[SSD_TITLE_ICON]);
}
static void handle_view_activate(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_activate);
ssd_update_padding(ssd);
ssd_update_parts(ssd, SSD_UPDATE_CAUSE_ACTIVATE);
}
static void handle_view_size(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_size);
ssd_update_parts(ssd, SSD_UPDATE_CAUSE_SIZE);
ssd->view_width = ssd->kywc_view->geometry.width;
ssd->view_height = ssd->kywc_view->geometry.height;
}
static void handle_view_tile(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_tile);
ssd_update_parts(ssd, SSD_UPDATE_CAUSE_TILE);
}
static void handle_view_title(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_title);
ssd_update_parts(ssd, SSD_UPDATE_CAUSE_TITLE);
}
static void handle_view_maximize(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_maximize);
ssd_update_parts(ssd, SSD_UPDATE_CAUSE_MAXIMIZE);
}
static void handle_view_fullscreen(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_fullscreen);
ssd_update_parts(ssd, SSD_UPDATE_CAUSE_FULLSCREEN);
}
static void handle_view_capabilities(struct wl_listener *listener, void *data)
{
struct kywc_view_capabilities_event *event = data;
if ((event->mask & (KYWC_VIEW_MAXIMIZE_BUTTON | KYWC_VIEW_MINIMIZE_BUTTON)) == 0) {
return;
}
struct ssd *ssd = wl_container_of(listener, ssd, view_capabilities);
uint32_t button_mask = ssd->buttons;
ssd_check_buttons(ssd);
if (button_mask != ssd->buttons) {
ssd_update_parts(ssd, SSD_UPDATE_CAUSE_CREATE);
}
}
static void ssd_check_buttons(struct ssd *ssd)
{
struct kywc_view *kywc_view = ssd->kywc_view;
/* always has a close button */
ssd->buttons = BUTTON_MASK_ALL;
ssd->button_count = 3;
if (!KYWC_VIEW_NEED_MAXIMIZE_BUTTON(kywc_view)) {
ssd->buttons &= ~BUTTON_MASK_MAXIMIZE;
ssd->button_count--;
}
if (!KYWC_VIEW_NEED_MINIMIZE_BUTTON(kywc_view)) {
ssd->buttons &= ~BUTTON_MASK_MINIMIZE;
ssd->button_count--;
}
}
static void ssd_parts_create(struct ssd *ssd)
{
if (ssd->created) {
return;
}
ssd->created = true;
ssd->view_width = ssd->view_height = 0;
struct kywc_view *kywc_view = ssd->kywc_view;
struct view *view = view_from_kywc_view(kywc_view);
ssd->tree = ky_scene_tree_create(view->tree);
ky_scene_node_lower_to_bottom(&ssd->tree->node);
ssd->view_size.notify = handle_view_size;
wl_signal_add(&kywc_view->events.size, &ssd->view_size);
ssd->view_tile.notify = handle_view_tile;
wl_signal_add(&kywc_view->events.tile, &ssd->view_tile);
ssd->view_maximize.notify = handle_view_maximize;
wl_signal_add(&kywc_view->events.maximize, &ssd->view_maximize);
ssd->view_fullscreen.notify = handle_view_fullscreen;
wl_signal_add(&kywc_view->events.fullscreen, &ssd->view_fullscreen);
ssd->view_capabilities.notify = handle_view_capabilities;
wl_signal_add(&kywc_view->events.capabilities, &ssd->view_capabilities);
wl_list_init(&ssd->view_activate.link);
wl_list_init(&ssd->view_title.link);
wl_list_init(&ssd->view_icon_update.link);
wl_list_init(&ssd->theme_update.link);
if (kywc_view->ssd & KYWC_SSD_TITLE) {
ssd->titlebar_tree = ky_scene_tree_create(ssd->tree);
/* buttons is subtree of titlebar, only need to set tree pos */
ssd->button_tree = ky_scene_tree_create(ssd->titlebar_tree);
ssd->view_title.notify = handle_view_title;
wl_signal_add(&kywc_view->events.title, &ssd->view_title);
ssd->view_icon_update.notify = handle_view_icon_update;
wl_signal_add(&view->events.icon_update, &ssd->view_icon_update);
ssd_check_buttons(ssd);
}
if (kywc_view->ssd & (KYWC_SSD_TITLE | KYWC_SSD_BORDER)) {
ssd->view_activate.notify = handle_view_activate;
wl_signal_add(&kywc_view->events.activate, &ssd->view_activate);
ssd->theme_update.notify = handle_theme_update;
theme_manager_add_update_listener(&ssd->theme_update, false);
}
/**
* detect scale by view geometry.
* it doesn't matter if setting to 1.0, scale will be set to best value
* in output_enter listener.
*/
ssd_create_parts(ssd, view->output->state.scale);
ssd_update_parts(ssd, SSD_UPDATE_CAUSE_ALL);
ssd->view_width = kywc_view->geometry.width;
ssd->view_height = kywc_view->geometry.height;
}
static void ssd_parts_destroy(struct ssd *ssd)
{
if (!ssd->created) {
return;
}
ssd->created = false;
wl_list_remove(&ssd->view_activate.link);
wl_list_remove(&ssd->view_size.link);
wl_list_remove(&ssd->view_tile.link);
wl_list_remove(&ssd->view_title.link);
wl_list_remove(&ssd->view_maximize.link);
wl_list_remove(&ssd->view_fullscreen.link);
wl_list_remove(&ssd->view_capabilities.link);
wl_list_remove(&ssd->theme_update.link);
wl_list_remove(&ssd->view_icon_update.link);
// XXX: destroyed in view_destroy, check ssd->tree ?
ky_scene_node_destroy(&ssd->tree->node);
}
static void handle_view_decoration(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_decoration);
ssd_update_margin(ssd);
ssd_update_padding(ssd);
/* view may not be mapped */
if (!ssd->kywc_view->mapped) {
return;
}
/* destroy first, may switched between extend_only and all */
ssd_parts_destroy(ssd);
if (ssd->kywc_view->ssd != KYWC_SSD_NONE) {
ssd_parts_create(ssd);
}
}
static void handle_view_map(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_map);
/* skip if not need ssd */
if (ssd->kywc_view->ssd == KYWC_SSD_NONE) {
return;
}
ssd_update_padding(ssd);
ssd_parts_create(ssd);
}
static void handle_view_unmap(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_unmap);
ssd_parts_destroy(ssd);
}
static void handle_view_destroy(struct wl_listener *listener, void *data)
{
struct ssd *ssd = wl_container_of(listener, ssd, view_destroy);
wl_list_remove(&ssd->view_destroy.link);
wl_list_remove(&ssd->view_decoration.link);
wl_list_remove(&ssd->view_map.link);
wl_list_remove(&ssd->view_unmap.link);
wl_list_remove(&ssd->link);
free(ssd);
}
static void handle_new_view(struct wl_listener *listener, void *data)
{
struct kywc_view *kywc_view = data;
struct ssd *ssd = calloc(1, sizeof(*ssd));
if (!ssd) {
return;
}
ssd->kywc_view = kywc_view;
wl_list_insert(&manager->ssds, &ssd->link);
ssd->view_decoration.notify = handle_view_decoration;
wl_signal_add(&kywc_view->events.decoration, &ssd->view_decoration);
ssd->view_map.notify = handle_view_map;
wl_signal_add(&kywc_view->events.map, &ssd->view_map);
ssd->view_unmap.notify = handle_view_unmap;
wl_signal_add(&kywc_view->events.unmap, &ssd->view_unmap);
ssd->view_destroy.notify = handle_view_destroy;
wl_signal_add(&kywc_view->events.destroy, &ssd->view_destroy);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->server_destroy.link);
wl_list_remove(&manager->new_view.link);
free(manager);
}
bool server_decoration_manager_create(struct view_manager *view_manager)
{
manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
wl_list_init(&manager->ssds);
wl_list_init(&manager->tooltips);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(view_manager->server, &manager->server_destroy);
manager->new_view.notify = handle_new_view;
kywc_view_add_new_listener(&manager->new_view);
return true;
}
kylin-wayland-compositor/src/view/ukui_startup.c 0000664 0001750 0001750 00000017347 15160461067 021145 0 ustar feng feng // SPDX-FileCopyrightText: 2025 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include "ukui-startup-v2-protocol.h"
#include "util/time.h"
#include "view_p.h"
#define UKUI_STARTUP_V2_VERSION 1
#define DEFAULT_RETIRE_TIME 5000
struct ukui_startup_management {
struct wl_global *global;
struct wl_list ukui_startup_infos;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct ukui_startup_info {
struct wl_list link;
struct wl_resource *resource;
const char *app_id;
pid_t pid;
struct kywc_box box;
uint32_t time_msec;
};
static struct ukui_startup_management *management = NULL;
static pid_t get_parent_pid(pid_t pid)
{
int ppid;
char path[256];
char line[256];
snprintf(path, sizeof(path), "/proc/%d/status", pid);
FILE *file = fopen(path, "r");
if (!file) {
kywc_log(KYWC_ERROR, "Failed to open /proc/[pid]/status");
return -1;
}
while (fgets(line, sizeof(line), file)) {
if (strncmp(line, "PPid:", 5) == 0) {
sscanf(line, "PPid: %d", &ppid);
fclose(file);
return ppid;
}
}
fclose(file);
return -1;
}
static void ukui_startup_info_destroy(struct ukui_startup_info *info)
{
/* resource may be destroyed in destroy request */
if (info->resource) {
wl_resource_set_user_data(info->resource, NULL);
}
wl_list_remove(&info->link);
free((void *)info->app_id);
free(info);
}
static void ukui_get_startup_geometry_recursive(pid_t pid, struct view *view)
{
struct ukui_startup_info *info, *tmp;
wl_list_for_each_reverse_safe(info, tmp, &management->ukui_startup_infos, link) {
if (info->pid == pid) {
view->startup_geometry = info->box;
ukui_startup_info_destroy(info);
return;
}
}
pid_t parent_pid = get_parent_pid(pid);
/* init/systemd (PID=1) */
if (parent_pid > 1) {
ukui_get_startup_geometry_recursive(parent_pid, view);
return;
}
if (!view->base.app_id) {
return;
}
wl_list_for_each_safe(info, tmp, &management->ukui_startup_infos, link) {
if (info->app_id && strcasecmp(info->app_id, view->base.app_id) == 0) {
kywc_log(KYWC_DEBUG, "Get startup_geometry from app_id %s", info->app_id);
view->startup_geometry = info->box;
ukui_startup_info_destroy(info);
return;
}
}
kywc_log(KYWC_INFO, "Failed to get startup_geometry for PID: %d, app_id: %s", pid,
view->base.app_id);
}
static void ukui_get_startup_geometry(struct view *view, void *data)
{
if (wl_list_empty(&management->ukui_startup_infos)) {
return;
}
/* remove retired info */
uint32_t time_now = current_time_msec();
struct ukui_startup_info *info, *tmp;
wl_list_for_each_safe(info, tmp, &management->ukui_startup_infos, link) {
if (time_now - info->time_msec > DEFAULT_RETIRE_TIME) {
ukui_startup_info_destroy(info);
}
}
if (wl_list_empty(&management->ukui_startup_infos)) {
return;
}
ukui_get_startup_geometry_recursive(view->pid, view);
}
static void handle_set_startup_geometry(struct wl_client *client, struct wl_resource *resource,
int32_t x, int32_t y, uint32_t width, uint32_t height)
{
struct ukui_startup_info *info = wl_resource_get_user_data(resource);
if (!info) {
return;
}
info->box = (struct kywc_box){ x, y, width, height };
}
static void handle_set_pid(struct wl_client *client, struct wl_resource *resource, uint32_t pid)
{
struct ukui_startup_info *info = wl_resource_get_user_data(resource);
if (!info) {
return;
}
info->pid = pid;
}
static void handle_set_appid(struct wl_client *client, struct wl_resource *resource,
const char *app_id)
{
struct ukui_startup_info *info = wl_resource_get_user_data(resource);
if (!info) {
return;
}
free((void *)info->app_id);
info->app_id = strdup(app_id);
}
static void handle_info_destroy(struct wl_client *client, struct wl_resource *resource)
{
struct ukui_startup_info *info = wl_resource_get_user_data(resource);
if (info) {
info->resource = NULL;
}
wl_resource_destroy(resource);
}
static const struct ukui_startup_info_v2_interface ukui_startup_info_v2_ipml = {
.set_startup_geometry = handle_set_startup_geometry,
.set_pid = handle_set_pid,
.set_appid = handle_set_appid,
.destroy = handle_info_destroy,
};
static void handle_create_startup_info(struct wl_client *client, struct wl_resource *resource,
uint32_t id)
{
struct ukui_startup_info *info = calloc(1, sizeof(*info));
if (!info) {
wl_client_post_no_memory(client);
return;
}
int version = wl_resource_get_version(resource);
struct wl_resource *info_resource =
wl_resource_create(client, &ukui_startup_info_v2_interface, version, id);
if (!resource) {
free(info);
wl_client_post_no_memory(client);
return;
}
info->resource = info_resource;
info->time_msec = current_time_msec();
wl_resource_set_implementation(info_resource, &ukui_startup_info_v2_ipml, info, NULL);
wl_list_insert(&management->ukui_startup_infos, &info->link);
}
static void handle_management_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct ukui_startup_management_v2_interface ukui_startup_management_v2_ipml = {
.destroy = handle_management_destroy,
.create_startup_info = handle_create_startup_info,
};
static void ukui_startup_management_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &ukui_startup_management_v2_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &ukui_startup_management_v2_ipml, management, NULL);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&management->display_destroy.link);
struct ukui_startup_info *info, *tmp;
wl_list_for_each_safe(info, tmp, &management->ukui_startup_infos, link) {
ukui_startup_info_destroy(info);
}
wl_global_destroy(management->global);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&management->server_destroy.link);
free(management);
management = NULL;
}
bool ukui_startup_management_create(struct view_manager *view_manager)
{
struct server *server = view_manager->server;
management = calloc(1, sizeof(*management));
if (!management) {
return false;
}
management->global =
wl_global_create(server->display, &ukui_startup_management_v2_interface,
UKUI_STARTUP_V2_VERSION, management, ukui_startup_management_bind);
if (!management->global) {
kywc_log(KYWC_WARN, "UKUI startup management create failed");
free(management);
management = NULL;
return false;
}
wl_list_init(&management->ukui_startup_infos);
view_manager->impl.get_startup_geometry = ukui_get_startup_geometry;
management->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &management->server_destroy);
management->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &management->display_destroy);
return true;
}
kylin-wayland-compositor/src/view/tablet_mode.c 0000664 0001750 0001750 00000055343 15160461067 020663 0 ustar feng feng // SPDX-FileCopyrightText: 2025 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include "effect/fade.h"
#include "output.h"
#include "theme.h"
#include "util/macros.h"
#include "view/workspace.h"
#include "view_p.h"
/* 0.15 is trigger window switcher threshold */
#define TRIGGER_SWITCHER_THRESHOLD (0.15)
struct tablet_mode_view {
struct wl_list link;
struct view *view;
struct wl_listener parent;
struct wl_listener view_update_capabilities;
bool remove_workspace;
struct ky_scene_rect *blur_rect;
};
struct task_bar {
struct view *view;
int default_y;
struct wl_listener output_geometry;
};
static struct tablet_mode_manager {
struct wl_list tablet_views;
struct task_bar task_bar;
struct view *pc_desktop;
struct view *tablet_desktop;
struct ky_scene_rect *global_blur_rect;
struct view_manager *view_manager;
struct wl_listener theme_pre_update;
} *manager = NULL;
/* chase kwin, some special app require special treatment */
#define TASK_BAR "tablet-taskbar"
#define PC_DESKTOP "peony-qt-desktop"
#define TABLET_DESKTOP "ukui-tablet-desktop"
static const char *special_appid[] = { "ukui-bluetooth", "kylin-nm", "ukui-search" };
static const char *special_title[] = { TASK_BAR };
static struct gesture {
enum gesture_type type;
enum gesture_stage stage;
uint8_t fingers;
uint32_t devices;
uint32_t directions;
uint32_t edges;
uint32_t follow_direction;
double follow_threshold;
char *desc;
struct gesture_binding *binding;
} gestures[] = {
{
GESTURE_TYPE_SWIPE,
GESTURE_STAGE_BEFORE,
1,
GESTURE_DEVICE_TOUCHSCREEN,
GESTURE_DIRECTION_UP,
GESTURE_EDGE_BOTTOM,
GESTURE_DIRECTION_UP,
0.0,
"follow finger up",
NULL,
},
{
GESTURE_TYPE_SWIPE,
GESTURE_STAGE_BEFORE,
1,
GESTURE_DEVICE_TOUCHSCREEN,
GESTURE_DIRECTION_UP,
GESTURE_EDGE_BOTTOM,
GESTURE_DIRECTION_DOWN,
0.0,
"follow finger down",
NULL,
},
{
GESTURE_TYPE_SWIPE,
GESTURE_STAGE_STOP,
1,
GESTURE_DEVICE_TOUCHSCREEN,
GESTURE_DIRECTION_NONE,
GESTURE_EDGE_BOTTOM,
GESTURE_DIRECTION_NONE,
0.0,
"follow finger stop",
NULL,
},
};
static void tablet_mode_view_maximized(struct view *view, bool maximized,
struct kywc_output *kywc_output);
static void tablet_mode_view_minimized(struct view *view, bool minimized);
static struct tablet_mode_view *tablet_mode_view_from_view(struct view *view)
{
return (struct tablet_mode_view *)view->mode_view;
}
static bool view_is_special(struct view *view)
{
for (uint32_t i = 0; i < ARRAY_SIZE(special_appid); i++) {
if (strcmp(view->base.app_id, special_appid[i]) == 0) {
return true;
}
}
for (uint32_t i = 0; i < ARRAY_SIZE(special_title); i++) {
if (strcmp(view->base.title, special_title[i]) == 0) {
return true;
}
}
return false;
}
static void task_bar_update_coord(void)
{
struct view *view = manager->task_bar.view;
// default y is %4 distance from bottom
struct output *output = output_from_kywc_output(view->output);
manager->task_bar.default_y = output->geometry.height - output->geometry.height * 0.04 -
view->base.geometry.height - view->base.margin.off_height;
}
static void handle_output_geometry(struct wl_listener *listener, void *data)
{
task_bar_update_coord();
}
static void tablet_mode_set_task_bar(struct view *view)
{
if (!view) {
manager->task_bar.view = NULL;
manager->task_bar.default_y = 0;
wl_list_remove(&manager->task_bar.output_geometry.link);
wl_list_init(&manager->task_bar.output_geometry.link);
return;
}
manager->task_bar.view = view;
task_bar_update_coord();
struct output *output = output_from_kywc_output(view->output);
wl_signal_add(&output->events.geometry, &manager->task_bar.output_geometry);
}
static void tablet_mode_task_bar_show(bool show)
{
struct view *view = manager->task_bar.view;
if (!view) {
return;
}
ky_scene_node_set_enabled(&view->tree->node, show);
}
static void handle_view_parent(struct wl_listener *listener, void *data)
{
struct tablet_mode_view *tablet_view = wl_container_of(listener, tablet_view, parent);
if (!tablet_view->view->base.mapped) {
return;
}
view_move_to_center(tablet_view->view);
}
static void tablet_mode_view_blur_rect_show(struct tablet_mode_view *tablet_view, bool show)
{
if (!tablet_view->blur_rect) {
return;
}
if (!show) {
ky_scene_node_set_enabled(&tablet_view->blur_rect->node, false);
return;
}
ky_scene_node_place_below(&tablet_view->blur_rect->node,
&tablet_view->view->current_proxy->tree->node);
ky_scene_node_set_enabled(&tablet_view->blur_rect->node, true);
}
static void tablet_mode_handle_view_update_capabilities(struct wl_listener *listener, void *data)
{
struct tablet_mode_view *tablet_view =
wl_container_of(listener, tablet_view, view_update_capabilities);
struct view *view = tablet_view->view;
struct view_update_capabilities_event *event = data;
if (event->mask & KYWC_VIEW_MAXIMIZABLE) {
if (view->parent) {
event->state &= ~KYWC_VIEW_MAXIMIZABLE;
}
}
if (event->mask & KYWC_VIEW_MAXIMIZE_BUTTON) {
event->state &= ~KYWC_VIEW_MAXIMIZE_BUTTON;
}
}
static struct view *tablet_mode_ancestor_from_view(struct view *view)
{
struct view *ancestor = view;
while (ancestor->parent) {
ancestor = ancestor->parent;
}
return ancestor;
}
static void tablet_mode_view_activate(struct view *view)
{
if (!KYWC_VIEW_IS_ACTIVATABLE(&view->base)) {
return;
}
if (view != manager->task_bar.view) {
tablet_mode_task_bar_show(false);
}
/* minimize the old activated view */
struct tablet_mode_view *tablet_view = tablet_mode_view_from_view(view);
struct view *activated_view = view_manager_get_activated();
if (tablet_view && activated_view) {
if (view != activated_view && !view_has_ancestor(view, activated_view) &&
!view_has_descendant(view, activated_view)) {
tablet_mode_view_minimized(tablet_mode_ancestor_from_view(activated_view), true);
}
}
/* activate the view */
struct view *descendant = view_find_descendant_modal(view);
view_do_activate(descendant ? descendant : view);
view_raise_to_top(view, true);
if (tablet_view) {
tablet_mode_view_blur_rect_show(tablet_view, true);
}
}
static void tablet_mode_view_blur_rect_create(struct tablet_mode_view *tablet_view)
{
if (tablet_view->blur_rect) {
return;
}
if (!tablet_view->view->current_proxy) {
return;
}
float color[4] = { 0.5, 0.5, 0.5, 0.5 };
struct output *output = output_from_kywc_output(tablet_view->view->output);
enum layer layer = tablet_view->view->base.kept_above
? LAYER_ABOVE
: (tablet_view->view->base.kept_below ? LAYER_BELOW : LAYER_NORMAL);
struct view_layer *view_layer =
workspace_layer(tablet_view->view->current_proxy->workspace, layer);
tablet_view->blur_rect = ky_scene_rect_create(view_layer->tree, output->geometry.width,
output->geometry.height, color);
if (!tablet_view->blur_rect) {
return;
}
pixman_region32_t region;
pixman_region32_init(®ion);
ky_scene_node_set_blur_region(&tablet_view->blur_rect->node, ®ion);
pixman_region32_fini(®ion);
ky_scene_node_place_below(&tablet_view->blur_rect->node,
&tablet_view->view->current_proxy->tree->node);
}
static void tablet_mode_view_blur_rect_destroy(struct tablet_mode_view *tablet_view)
{
if (!tablet_view->blur_rect) {
return;
}
ky_scene_node_destroy(&tablet_view->blur_rect->node);
tablet_view->blur_rect = NULL;
}
static struct tablet_mode_view *tablet_mode_view_create(struct view *view)
{
struct tablet_mode_view *tablet_view = calloc(1, sizeof(*tablet_view));
if (!tablet_view) {
return NULL;
}
wl_list_insert(&manager->tablet_views, &tablet_view->link);
tablet_view->view = view;
tablet_view->parent.notify = handle_view_parent;
wl_signal_add(&view->events.parent, &tablet_view->parent);
tablet_view->view_update_capabilities.notify = tablet_mode_handle_view_update_capabilities;
view_add_update_capabilities_listener(view, &tablet_view->view_update_capabilities);
return tablet_view;
}
static void tablet_mode_view_destroy(struct tablet_mode_view *tablet_view)
{
wl_list_remove(&tablet_view->link);
wl_list_remove(&tablet_view->parent.link);
wl_list_remove(&tablet_view->view_update_capabilities.link);
tablet_mode_view_blur_rect_destroy(tablet_view);
free(tablet_view);
}
static void tablet_mode_view_apply_minimized(struct view *view, bool minimized)
{
view_do_minimized(view, minimized);
}
static void tablet_mode_view_minimized(struct view *view, bool minimized)
{
struct tablet_mode_view *tablet_view = tablet_mode_view_from_view(view);
if (!tablet_view || !KYWC_VIEW_IS_MINIMIZABLE(&view->base)) {
return;
}
struct view *ancestor = tablet_mode_ancestor_from_view(view);
if (!minimized && !view->base.maximized) {
// restore minimized view and then maximize the view
tablet_mode_view_apply_minimized(ancestor, minimized);
tablet_mode_view_maximized(ancestor, true, ancestor->output);
return;
}
tablet_mode_view_blur_rect_show(tablet_view, !minimized);
tablet_mode_view_apply_minimized(minimized ? view : ancestor, minimized);
}
static void tablet_mode_view_maximized(struct view *view, bool maximized,
struct kywc_output *kywc_output)
{
struct tablet_mode_view *tablet_view = tablet_mode_view_from_view(view);
if (!tablet_view || !maximized) {
return;
}
if (KYWC_VIEW_IS_MAXIMIZABLE(&view->base)) {
view_do_maximized(view, maximized, kywc_output);
} else {
view_move_to_center(view);
if (!view->parent) {
/* need blur if view can not maximized*/
tablet_mode_view_blur_rect_create(tablet_view);
}
}
}
static void tablet_mode_view_map(struct view *view)
{
positioner_add_new_view(view);
if (!manager->task_bar.view) {
if (strcmp(view->base.title, TASK_BAR) == 0) {
tablet_mode_set_task_bar(view);
tablet_mode_task_bar_show(false);
}
}
if (!manager->tablet_desktop) {
if (strcmp(view->base.title, TABLET_DESKTOP) == 0) {
manager->tablet_desktop = view;
}
}
if (!manager->pc_desktop) {
if (strcmp(view->base.app_id, PC_DESKTOP) == 0) {
manager->pc_desktop = view;
}
}
if (view->base.role != KYWC_VIEW_ROLE_NORMAL || view_is_special(view)) {
return;
}
view->mode_view = tablet_mode_view_create(view);
tablet_mode_view_maximized(view, true, view->output);
}
static void tablet_mode_view_unmap(struct view *view)
{
if (view == manager->task_bar.view) {
tablet_mode_set_task_bar(NULL);
} else if (view == manager->tablet_desktop) {
manager->tablet_desktop = NULL;
} else if (view == manager->pc_desktop) {
manager->pc_desktop = NULL;
}
struct tablet_mode_view *tablet_view = tablet_mode_view_from_view(view);
if (!tablet_view) {
return;
}
tablet_mode_view_destroy(tablet_view);
view->mode_view = NULL;
}
static void global_blur_rect_create(void)
{
if (manager->global_blur_rect) {
return;
}
float color[4] = { 0.1, 0.1, 0.1, 0.1 };
struct output *output = input_current_output(input_manager_get_default_seat());
struct view_layer *layer = view_manager_get_layer(LAYER_ON_SCREEN_DISPLAY, false);
manager->global_blur_rect =
ky_scene_rect_create(layer->tree, output->geometry.width, output->geometry.height, color);
if (!manager->global_blur_rect) {
return;
}
pixman_region32_t region;
pixman_region32_init_rect(®ion, output->geometry.x, output->geometry.y,
output->geometry.width, output->geometry.height);
ky_scene_node_set_blur_region(&manager->global_blur_rect->node, ®ion);
pixman_region32_fini(®ion);
}
static void global_blur_rect_destroy(void)
{
if (!manager->global_blur_rect) {
return;
}
ky_scene_node_destroy(&manager->global_blur_rect->node);
manager->global_blur_rect = NULL;
}
static void global_blur_rect_set_blur(float percent)
{
if (!manager->global_blur_rect) {
global_blur_rect_create();
}
if (!manager->global_blur_rect) {
return;
}
if (percent == 0.0) {
ky_scene_node_set_enabled(&manager->global_blur_rect->node, false);
return;
}
/* offset 0-10 */
float offset = percent * 10.0 / 1.0;
ky_scene_node_set_blur_level(&manager->global_blur_rect->node, 2, offset);
ky_scene_node_set_enabled(&manager->global_blur_rect->node, true);
}
static void gestures_action(struct gesture_binding *binding, void *data, double dx, double dy)
{
struct gesture *gesture = data;
struct output *output = input_current_output(input_manager_get_default_seat());
struct view *view = view_manager_get_activated();
struct view *task_bar = manager->task_bar.view;
if (!task_bar) {
return;
}
// task bar follow finger if activated view is normal view
if (tablet_mode_view_from_view(view) || view == task_bar) {
if (gesture->stage == GESTURE_STAGE_STOP) {
int threshold = output->geometry.height - task_bar->base.geometry.height / 2;
if (task_bar->base.geometry.y > threshold) {
tablet_mode_task_bar_show(false);
} else {
view_do_move(task_bar, task_bar->base.geometry.x, manager->task_bar.default_y);
}
/* hide task bar if trigger switcher */
if (dy <= -TRIGGER_SWITCHER_THRESHOLD) {
tablet_mode_task_bar_show(false);
}
return;
}
if (!task_bar->base.activated) {
view_do_activate(task_bar);
view_raise_to_top(task_bar, false);
}
// calculate follow y
int y = output->geometry.y + output->geometry.height + dy * output->geometry.height;
y = y < manager->task_bar.default_y ? manager->task_bar.default_y : y;
tablet_mode_task_bar_show(true);
view_do_move(task_bar, task_bar->base.geometry.x, y);
return;
}
/* set blur */
if (gesture->stage == GESTURE_STAGE_STOP) {
global_blur_rect_set_blur(0.0);
return;
}
dy = CLAMP(dy, -TRIGGER_SWITCHER_THRESHOLD, 0.0);
global_blur_rect_set_blur(-dy / TRIGGER_SWITCHER_THRESHOLD);
}
static void tablet_mode_update_or_restore_theme(bool update)
{
struct theme *theme = theme_manager_get_theme();
if (update) {
theme->icon_size = 32;
theme->button_width = 40;
theme->title_height = 64;
theme->subtitle_height = 48;
return;
}
theme->icon_size = 24;
theme->button_width = 32;
theme->title_height = 38;
theme->subtitle_height = 38;
}
static void tablet_mode_hanlde_theme_pre_update(struct wl_listener *listener, void *data)
{
struct theme_update_event *event = data;
if (event->update_mask & THEME_UPDATE_MASK_DECORATION_SIZE) {
event->update_mask &= ~THEME_UPDATE_MASK_DECORATION_SIZE;
tablet_mode_update_or_restore_theme(true);
}
}
static void tablet_mode_input_actions_block(bool enable)
{
kywc_key_binding_block_type(KEY_BINDING_TYPE_WIN_MENU, enable);
kywc_key_binding_block_type(KEY_BINDING_TYPE_SWITCH_WORKSPACE, enable);
}
static void input_actions_register(void)
{
for (size_t i = 0; i < ARRAY_SIZE(gestures); i++) {
struct gesture *gesture = &gestures[i];
if (gesture->binding) {
kywc_gesture_binding_destroy(gesture->binding);
}
struct gesture_binding *binding = kywc_gesture_binding_create(
gesture->type, gesture->stage, gesture->devices, gesture->directions, gesture->edges,
gesture->fingers, gesture->follow_direction, gesture->follow_threshold, gesture->desc);
if (!binding) {
continue;
}
if (!kywc_gesture_binding_register(binding, gestures_action, gesture)) {
kywc_gesture_binding_destroy(binding);
continue;
}
gesture->binding = binding;
}
}
static void input_actions_unregister(void)
{
for (size_t i = 0; i < ARRAY_SIZE(gestures); i++) {
struct gesture *gesture = &gestures[i];
if (gesture->binding) {
kywc_gesture_binding_destroy(gesture->binding);
}
gesture->binding = NULL;
}
}
static void tablet_mode_enter(void)
{
/* update theme */
tablet_mode_update_or_restore_theme(true);
theme_manager_update_theme(THEME_UPDATE_MASK_DECORATION_SIZE);
theme_manager_add_update_listener(&manager->theme_pre_update, true);
/* input actions */
tablet_mode_input_actions_block(true);
input_actions_register();
struct view *view, *maximize_view = NULL;
struct view_manager *view_manager = manager->view_manager;
struct view *activated_view = view_manager_get_activated();
wl_list_for_each(view, &view_manager->views, link) {
if (!view->base.mapped) {
continue;
}
if (strcmp(view->base.title, TASK_BAR) == 0) {
tablet_mode_set_task_bar(view);
tablet_mode_task_bar_show(false);
} else if (strcmp(view->base.title, TABLET_DESKTOP) == 0) {
manager->tablet_desktop = view;
} else if (strcmp(view->base.app_id, PC_DESKTOP) == 0) {
manager->pc_desktop = view;
}
if (view->base.role != KYWC_VIEW_ROLE_NORMAL || view_is_special(view)) {
continue;
}
view->mode_view = tablet_mode_view_create(view);
/* workspace */
struct workspace *workspace = workspace_manager_get_current();
struct tablet_mode_view *tablet_view = tablet_mode_view_from_view(view);
tablet_view->remove_workspace = workspace != view->current_proxy->workspace;
view_add_workspace(view, workspace);
if (view == activated_view) {
maximize_view = view;
continue;
}
if (view_has_ancestor(view, activated_view)) {
view_move_to_center(view);
continue;
}
if (view_has_descendant(view, activated_view)) {
tablet_mode_view_maximized(view, true, view->output);
continue;
}
if (view_has_modal_property(view)) {
tablet_mode_view_maximized(view, true, view->output);
continue;
}
tablet_mode_view_minimized(view, true);
}
if (manager->pc_desktop) {
view_add_fade_effect(manager->pc_desktop, FADE_OUT);
ky_scene_node_set_enabled(&manager->pc_desktop->tree->node, false);
}
if (manager->tablet_desktop) {
ky_scene_node_set_enabled(&manager->tablet_desktop->tree->node, true);
view_add_fade_effect(manager->tablet_desktop, FADE_IN);
}
if (maximize_view) {
view_raise_to_top(maximize_view, true);
tablet_mode_view_maximized(maximize_view, true, maximize_view->output);
}
}
static void tablet_mode_leave(void)
{
/* desktop and task bar */
tablet_mode_task_bar_show(false);
tablet_mode_set_task_bar(NULL);
if (manager->tablet_desktop) {
view_add_fade_effect(manager->tablet_desktop, FADE_OUT);
ky_scene_node_set_enabled(&manager->tablet_desktop->tree->node, false);
manager->tablet_desktop = NULL;
}
if (manager->pc_desktop) {
ky_scene_node_set_enabled(&manager->pc_desktop->tree->node, true);
view_add_fade_effect(manager->pc_desktop, FADE_IN);
manager->pc_desktop = NULL;
}
/* input actions */
tablet_mode_input_actions_block(false);
input_actions_unregister();
/* global blur rect */
global_blur_rect_destroy();
struct tablet_mode_view *tablet_view, *tmp;
wl_list_for_each_safe(tablet_view, tmp, &manager->tablet_views, link) {
if (tablet_view->remove_workspace) {
view_remove_workspace(tablet_view->view, tablet_view->view->current_proxy->workspace);
}
tablet_view->view->mode_view = NULL;
tablet_mode_view_destroy(tablet_view);
}
wl_list_remove(&manager->theme_pre_update.link);
tablet_mode_update_or_restore_theme(false);
theme_manager_update_theme(THEME_UPDATE_MASK_DECORATION_SIZE);
}
static void tablet_mode_view_click(struct seat *seat, struct view *view, uint32_t button,
bool pressed, enum click_state state)
{
/* active current view */
kywc_view_activate(&view->base);
view_set_focus(view, seat);
}
static void tablet_mode_destroy(void)
{
if (!manager) {
return;
}
struct tablet_mode_view *tablet_view, *tmp;
wl_list_for_each_safe(tablet_view, tmp, &manager->tablet_views, link) {
tablet_mode_view_destroy(tablet_view);
}
free(manager);
manager = NULL;
}
static const struct view_mode_interface tablet_mode_impl = {
.name = "tablet_mode",
.view_map = tablet_mode_view_map,
.view_unmap = tablet_mode_view_unmap,
.view_request_move = NULL,
.view_request_resize = NULL,
.view_request_minimized = tablet_mode_view_minimized,
.view_request_maximized = tablet_mode_view_maximized,
.view_request_fullscreen = NULL,
.view_request_tiled = NULL,
.view_request_activate = tablet_mode_view_activate,
.view_request_show_menu = NULL,
.view_click = tablet_mode_view_click,
.view_hover = NULL,
.view_mode_enter = tablet_mode_enter,
.view_mode_leave = tablet_mode_leave,
.mode_destroy = tablet_mode_destroy,
};
void tablet_mode_register(struct view_manager *view_manager)
{
struct view_mode *mode = view_manager_mode_register(&tablet_mode_impl);
if (!mode) {
return;
}
manager = calloc(1, sizeof(*manager));
if (!manager) {
view_manager_mode_unregister(mode);
return;
}
wl_list_init(&manager->tablet_views);
wl_list_init(&manager->task_bar.output_geometry.link);
manager->view_manager = view_manager;
manager->theme_pre_update.notify = tablet_mode_hanlde_theme_pre_update;
manager->task_bar.output_geometry.notify = handle_output_geometry;
tablet_mode_set_task_bar(NULL);
}
kylin-wayland-compositor/src/view/ky_toplevel.c 0000664 0001750 0001750 00000067101 15160460057 020732 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include "kywc-toplevel-v1-protocol.h"
#include "theme.h"
#include "view/workspace.h"
#include "view_p.h"
struct ky_toplevel_manager {
struct wl_event_loop *event_loop;
struct wl_global *global;
struct wl_list resources;
struct wl_list toplevels;
struct wl_listener new_mapped_toplevel;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct ky_toplevel {
struct ky_toplevel_manager *manager;
struct wl_list link;
struct wl_event_source *idle_source;
struct wl_list resources;
const char *icon_name;
struct kywc_view *view;
struct wl_listener unmap;
struct wl_listener title;
struct wl_listener app_id;
struct wl_listener maximize;
struct wl_listener minimize;
struct wl_listener activate;
struct wl_listener fullscreen;
struct wl_listener position;
struct wl_listener size;
struct wl_listener icon_update;
struct wl_listener output;
struct wl_listener parent;
struct wl_listener workspace_enter;
struct wl_listener workspace_leave;
};
static struct ky_toplevel *toplevel_for_view(struct ky_toplevel_manager *manager,
struct kywc_view *view)
{
struct ky_toplevel *toplevel;
wl_list_for_each(toplevel, &manager->toplevels, link) {
if (toplevel->view == view) {
return toplevel;
}
}
return NULL;
}
static void toplevel_update_icon_name(struct ky_toplevel *toplevel)
{
struct view *view = view_from_kywc_view(toplevel->view);
const char *icon_name = theme_icon_get_name(view->icon);
if (toplevel->icon_name && strcmp(toplevel->icon_name, icon_name) == 0) {
return;
}
free((void *)toplevel->icon_name);
toplevel->icon_name = strdup(icon_name);
}
static void toplevel_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void toplevel_handle_set_maximized(struct wl_client *client, struct wl_resource *resource,
const char *output)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
struct kywc_output *kywc_output = NULL;
if (output) {
kywc_output = kywc_output_by_uuid(output);
}
kywc_view_set_maximized(toplevel->view, true, kywc_output);
}
static void toplevel_handle_unset_maximized(struct wl_client *client, struct wl_resource *resource)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
kywc_view_set_maximized(toplevel->view, false, NULL);
}
static void toplevel_handle_set_minimized(struct wl_client *client, struct wl_resource *resource)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
kywc_view_set_minimized(toplevel->view, true);
}
static void toplevel_handle_unset_minimized(struct wl_client *client, struct wl_resource *resource)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
kywc_view_set_minimized(toplevel->view, false);
}
static void toplevel_handle_set_fullscreen(struct wl_client *client, struct wl_resource *resource,
const char *output)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
struct kywc_output *kywc_output = NULL;
if (output) {
kywc_output = kywc_output_by_uuid(output);
}
kywc_view_set_fullscreen(toplevel->view, true, kywc_output);
}
static void toplevel_handle_unset_fullscreen(struct wl_client *client, struct wl_resource *resource)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
kywc_view_set_fullscreen(toplevel->view, false, NULL);
}
static void toplevel_handle_activate(struct wl_client *client, struct wl_resource *resource)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
kywc_view_activate(toplevel->view);
view_set_focus(view_from_kywc_view(toplevel->view), input_manager_get_default_seat());
}
static void toplevel_handle_close(struct wl_client *client, struct wl_resource *resource)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
kywc_view_close(toplevel->view);
}
static void toplevel_handle_enter_workspace(struct wl_client *client, struct wl_resource *resource,
const char *id)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
struct workspace *workspace = workspace_by_uuid(id);
if (workspace) {
view_add_workspace(view_from_kywc_view(toplevel->view), workspace);
}
}
static void toplevel_handle_leave_workspace(struct wl_client *client, struct wl_resource *resource,
const char *id)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
struct workspace *workspace = workspace_by_uuid(id);
if (workspace) {
view_remove_workspace(view_from_kywc_view(toplevel->view), workspace);
}
}
static void toplevel_handle_move_to_workspace(struct wl_client *client,
struct wl_resource *resource, const char *id)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
struct workspace *workspace = workspace_by_uuid(id);
if (workspace) {
view_set_workspace(view_from_kywc_view(toplevel->view), workspace);
}
}
static void toplevel_handle_move_to_output(struct wl_client *client, struct wl_resource *resource,
const char *id)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
struct kywc_output *kywc_output = kywc_output_by_uuid(id);
if (kywc_output) {
view_move_to_output(view_from_kywc_view(toplevel->view), NULL, NULL, kywc_output);
}
}
static void toplevel_handle_set_position(struct wl_client *client, struct wl_resource *resource,
int32_t x, int32_t y)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
kywc_view_move(toplevel->view, x + toplevel->view->margin.off_x,
y + toplevel->view->margin.off_y);
}
static void toplevel_handle_set_size(struct wl_client *client, struct wl_resource *resource,
uint32_t width, uint32_t height)
{
struct ky_toplevel *toplevel = wl_resource_get_user_data(resource);
if (!toplevel) {
return;
}
struct kywc_box geo = { toplevel->view->geometry.x, toplevel->view->geometry.y,
width - toplevel->view->margin.off_width,
height - toplevel->view->margin.off_height };
kywc_view_resize(toplevel->view, &geo);
}
static const struct kywc_toplevel_v1_interface ky_toplevel_impl = {
.destroy = toplevel_handle_destroy,
.set_maximized = toplevel_handle_set_maximized,
.unset_maximized = toplevel_handle_unset_maximized,
.set_minimized = toplevel_handle_set_minimized,
.unset_minimized = toplevel_handle_unset_minimized,
.set_fullscreen = toplevel_handle_set_fullscreen,
.unset_fullscreen = toplevel_handle_unset_fullscreen,
.activate = toplevel_handle_activate,
.close = toplevel_handle_close,
.enter_workspace = toplevel_handle_enter_workspace,
.leave_workspace = toplevel_handle_leave_workspace,
.move_to_workspace = toplevel_handle_move_to_workspace,
.move_to_output = toplevel_handle_move_to_output,
.set_position = toplevel_handle_set_position,
.set_size = toplevel_handle_set_size,
};
static void ky_toplevel_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
}
static struct wl_resource *
create_toplevel_resource_for_resource(struct ky_toplevel *ky_toplevel,
struct wl_resource *manager_resource)
{
struct wl_client *client = wl_resource_get_client(manager_resource);
struct wl_resource *resource = wl_resource_create(client, &kywc_toplevel_v1_interface,
wl_resource_get_version(manager_resource), 0);
if (!resource) {
wl_client_post_no_memory(client);
return NULL;
}
wl_resource_set_implementation(resource, &ky_toplevel_impl, ky_toplevel,
ky_toplevel_resource_destroy);
wl_list_insert(&ky_toplevel->resources, wl_resource_get_link(resource));
kywc_toplevel_manager_v1_send_toplevel(manager_resource, resource, ky_toplevel->view->uuid);
return resource;
}
static uint32_t toplevel_state(struct kywc_view *kywc_view)
{
uint32_t state = 0;
if (kywc_view->maximized) {
state |= KYWC_TOPLEVEL_V1_STATE_MAXIMIZED;
}
if (kywc_view->minimized) {
state |= KYWC_TOPLEVEL_V1_STATE_MINIMIZED;
}
if (kywc_view->activated) {
state |= KYWC_TOPLEVEL_V1_STATE_ACTIVATED;
}
if (kywc_view->fullscreen) {
state |= KYWC_TOPLEVEL_V1_STATE_FULLSCREEN;
}
return state;
}
static uint32_t toplevel_caps(struct kywc_view *kywc_view)
{
uint32_t caps = 0;
if (kywc_view->skip_taskbar) {
caps |= KYWC_TOPLEVEL_V1_CAPABILITY_SKIP_TASKBAR;
}
if (kywc_view->skip_switcher) {
caps |= KYWC_TOPLEVEL_V1_CAPABILITY_SKIP_SWITCHER;
}
return caps;
}
static void toplevel_send_details_to_toplevel_resource(struct ky_toplevel *toplevel,
struct wl_resource *resource)
{
struct kywc_view *kywc_view = toplevel->view;
struct view *view = view_from_kywc_view(kywc_view);
if (kywc_view->app_id) {
kywc_toplevel_v1_send_app_id(resource, kywc_view->app_id);
}
if (kywc_view->title) {
kywc_toplevel_v1_send_title(resource, kywc_view->title);
}
if (view->output) {
kywc_toplevel_v1_send_primary_output(resource, view->output->uuid);
}
kywc_toplevel_v1_send_pid(resource, view->pid);
/* all workspaces the toplevel in */
struct view_proxy *proxy;
wl_list_for_each(proxy, &view->view_proxies, view_link) {
kywc_toplevel_v1_send_workspace_enter(resource, proxy->workspace->uuid);
}
kywc_toplevel_v1_send_capabilities(resource, toplevel_caps(toplevel->view));
kywc_toplevel_v1_send_state(resource, toplevel_state(toplevel->view));
if (view->parent) {
struct ky_toplevel *parent_toplevel =
toplevel_for_view(toplevel->manager, &view->parent->base);
if (parent_toplevel) {
struct wl_resource *parent_resource = wl_resource_find_for_client(
&parent_toplevel->resources, wl_resource_get_client(resource));
if (parent_resource) {
kywc_toplevel_v1_send_parent(resource, parent_resource);
}
}
} else {
kywc_toplevel_v1_send_parent(resource, NULL);
}
kywc_toplevel_v1_send_icon(resource, toplevel->icon_name);
int32_t x = kywc_view->geometry.x - kywc_view->margin.off_x;
int32_t y = kywc_view->geometry.y - kywc_view->margin.off_y;
uint32_t width = kywc_view->geometry.width + kywc_view->margin.off_width;
uint32_t height = kywc_view->geometry.height + kywc_view->margin.off_height;
kywc_toplevel_v1_send_geometry(resource, x, y, width, height);
kywc_toplevel_v1_send_done(resource);
}
static void manager_handle_stop(struct wl_client *client, struct wl_resource *resource)
{
kywc_toplevel_manager_v1_send_finished(resource);
wl_resource_destroy(resource);
}
static const struct kywc_toplevel_manager_v1_interface ky_toplevel_manager_impl = {
.stop = manager_handle_stop,
};
static void ky_toplevel_manager_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
}
static void ky_toplevel_manager_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct ky_toplevel_manager *manager = data;
struct wl_resource *resource =
wl_resource_create(client, &kywc_toplevel_manager_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &ky_toplevel_manager_impl, manager,
ky_toplevel_manager_resource_destroy);
wl_list_insert(&manager->resources, wl_resource_get_link(resource));
/* send all toplevels and details */
struct ky_toplevel *toplevel, *tmp;
wl_list_for_each_reverse_safe(toplevel, tmp, &manager->toplevels, link) {
create_toplevel_resource_for_resource(toplevel, resource);
}
wl_list_for_each_reverse_safe(toplevel, tmp, &manager->toplevels, link) {
struct wl_resource *toplevel_resource =
wl_resource_find_for_client(&toplevel->resources, client);
toplevel_send_details_to_toplevel_resource(toplevel, toplevel_resource);
}
}
static void toplevel_idle_send_done(void *data)
{
struct ky_toplevel *toplevel = data;
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_done(resource);
}
toplevel->idle_source = NULL;
}
static void toplevel_update_idle_source(struct ky_toplevel *toplevel)
{
if (toplevel->idle_source || wl_list_empty(&toplevel->resources)) {
return;
}
toplevel->idle_source =
wl_event_loop_add_idle(toplevel->manager->event_loop, toplevel_idle_send_done, toplevel);
}
static void handle_toplevel_title(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, title);
if (!toplevel->view->title) {
return;
}
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_title(resource, toplevel->view->title);
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_app_id(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, app_id);
if (!toplevel->view->app_id) {
return;
}
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_app_id(resource, toplevel->view->app_id);
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_icon_update(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, icon_update);
struct view *view = view_from_kywc_view(toplevel->view);
if (theme_icon_is_fallback(view->icon)) {
return;
}
toplevel_update_icon_name(toplevel);
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_icon(resource, toplevel->icon_name);
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_maximize(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, maximize);
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_state(resource, toplevel_state(toplevel->view));
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_minimize(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, minimize);
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_state(resource, toplevel_state(toplevel->view));
}
toplevel_update_idle_source(toplevel);
}
static void toplevel_raise_children(struct ky_toplevel *toplevel, struct ky_toplevel *reference)
{
struct view *view = view_from_kywc_view(toplevel->view);
struct ky_toplevel *child_toplevel;
struct view *child;
wl_list_for_each(child, &view->children, parent_link) {
if (reference && reference->view == &child->base) {
continue;
}
child_toplevel = toplevel_for_view(toplevel->manager, &child->base);
if (!child_toplevel) {
continue;
}
wl_list_remove(&child_toplevel->link);
wl_list_insert(&toplevel->manager->toplevels, &child_toplevel->link);
toplevel_raise_children(child_toplevel, NULL);
}
}
static void toplevel_raise_with_parent(struct ky_toplevel *toplevel, struct ky_toplevel *reference)
{
struct view *view = view_from_kywc_view(toplevel->view);
if (view->parent) {
struct ky_toplevel *parent_toplevel =
toplevel_for_view(toplevel->manager, &view->parent->base);
if (parent_toplevel) {
toplevel_raise_with_parent(parent_toplevel, toplevel);
}
}
wl_list_remove(&toplevel->link);
wl_list_insert(&toplevel->manager->toplevels, &toplevel->link);
toplevel_raise_children(toplevel, reference);
}
static void handle_toplevel_activate(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, activate);
/* raise parent toplevel first */
if (toplevel->view->activated) {
toplevel_raise_with_parent(toplevel, NULL);
}
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_state(resource, toplevel_state(toplevel->view));
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_fullscreen(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, fullscreen);
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_state(resource, toplevel_state(toplevel->view));
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_position(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, position);
struct kywc_view *view = toplevel->view;
int32_t x = view->geometry.x - view->margin.off_x;
int32_t y = view->geometry.y - view->margin.off_y;
uint32_t width = view->geometry.width + view->margin.off_width;
uint32_t height = view->geometry.height + view->margin.off_height;
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_geometry(resource, x, y, width, height);
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_size(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, size);
struct kywc_view *view = toplevel->view;
int32_t x = view->geometry.x - view->margin.off_x;
int32_t y = view->geometry.y - view->margin.off_y;
uint32_t width = view->geometry.width + view->margin.off_width;
uint32_t height = view->geometry.height + view->margin.off_height;
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_geometry(resource, x, y, width, height);
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_output(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, output);
struct view *view = view_from_kywc_view(toplevel->view);
if (!view->output) {
return;
}
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_primary_output(resource, view->output->uuid);
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_parent(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, parent);
struct view *view = view_from_kywc_view(toplevel->view);
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
if (view->parent) {
struct ky_toplevel *parent_toplevel =
toplevel_for_view(toplevel->manager, &view->parent->base);
if (parent_toplevel) {
struct wl_resource *parent_resource = wl_resource_find_for_client(
&parent_toplevel->resources, wl_resource_get_client(resource));
if (parent_resource) {
kywc_toplevel_v1_send_parent(resource, parent_resource);
}
}
} else {
kywc_toplevel_v1_send_parent(resource, NULL);
}
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_workspace_enter(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, workspace_enter);
struct workspace *workspace = data;
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_workspace_enter(resource, workspace->uuid);
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_workspace_leave(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, workspace_leave);
struct workspace *workspace = data;
struct wl_resource *resource;
wl_resource_for_each(resource, &toplevel->resources) {
kywc_toplevel_v1_send_workspace_leave(resource, workspace->uuid);
}
toplevel_update_idle_source(toplevel);
}
static void handle_toplevel_unmap(struct wl_listener *listener, void *data);
static void handle_new_mapped_toplevel(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = calloc(1, sizeof(*toplevel));
if (!toplevel) {
return;
}
struct ky_toplevel_manager *manager = wl_container_of(listener, manager, new_mapped_toplevel);
toplevel->manager = manager;
wl_list_init(&toplevel->resources);
wl_list_insert(&manager->toplevels, &toplevel->link);
struct kywc_view *kywc_view = data;
toplevel->view = kywc_view;
toplevel->unmap.notify = handle_toplevel_unmap;
wl_signal_add(&kywc_view->events.unmap, &toplevel->unmap);
toplevel->title.notify = handle_toplevel_title;
wl_signal_add(&toplevel->view->events.title, &toplevel->title);
toplevel->app_id.notify = handle_toplevel_app_id;
wl_signal_add(&toplevel->view->events.app_id, &toplevel->app_id);
toplevel->maximize.notify = handle_toplevel_maximize;
wl_signal_add(&toplevel->view->events.maximize, &toplevel->maximize);
toplevel->minimize.notify = handle_toplevel_minimize;
wl_signal_add(&toplevel->view->events.minimize, &toplevel->minimize);
toplevel->activate.notify = handle_toplevel_activate;
wl_signal_add(&toplevel->view->events.activate, &toplevel->activate);
toplevel->fullscreen.notify = handle_toplevel_fullscreen;
wl_signal_add(&toplevel->view->events.fullscreen, &toplevel->fullscreen);
toplevel->position.notify = handle_toplevel_position;
wl_signal_add(&toplevel->view->events.position, &toplevel->position);
toplevel->size.notify = handle_toplevel_size;
wl_signal_add(&toplevel->view->events.size, &toplevel->size);
struct view *view = view_from_kywc_view(toplevel->view);
toplevel->icon_update.notify = handle_toplevel_icon_update;
wl_signal_add(&view->events.icon_update, &toplevel->icon_update);
toplevel->output.notify = handle_toplevel_output;
wl_signal_add(&view->events.output, &toplevel->output);
toplevel->parent.notify = handle_toplevel_parent;
wl_signal_add(&view->events.parent, &toplevel->parent);
toplevel->workspace_enter.notify = handle_toplevel_workspace_enter;
wl_signal_add(&view->events.workspace_enter, &toplevel->workspace_enter);
toplevel->workspace_leave.notify = handle_toplevel_workspace_leave;
wl_signal_add(&view->events.workspace_leave, &toplevel->workspace_leave);
toplevel_update_icon_name(toplevel);
/* send toplevel and detail */
struct wl_resource *resource, *toplevel_resource;
wl_resource_for_each(resource, &toplevel->manager->resources) {
toplevel_resource = create_toplevel_resource_for_resource(toplevel, resource);
toplevel_send_details_to_toplevel_resource(toplevel, toplevel_resource);
}
}
static void handle_toplevel_unmap(struct wl_listener *listener, void *data)
{
struct ky_toplevel *toplevel = wl_container_of(listener, toplevel, unmap);
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &toplevel->resources) {
kywc_toplevel_v1_send_closed(resource);
// make the resource inert
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
wl_resource_set_user_data(resource, NULL);
}
if (toplevel->idle_source) {
wl_event_source_remove(toplevel->idle_source);
}
wl_list_remove(&toplevel->title.link);
wl_list_remove(&toplevel->app_id.link);
wl_list_remove(&toplevel->icon_update.link);
wl_list_remove(&toplevel->maximize.link);
wl_list_remove(&toplevel->minimize.link);
wl_list_remove(&toplevel->activate.link);
wl_list_remove(&toplevel->fullscreen.link);
wl_list_remove(&toplevel->position.link);
wl_list_remove(&toplevel->size.link);
wl_list_remove(&toplevel->output.link);
wl_list_remove(&toplevel->parent.link);
wl_list_remove(&toplevel->workspace_enter.link);
wl_list_remove(&toplevel->workspace_leave.link);
free((void *)toplevel->icon_name);
toplevel->icon_name = NULL;
wl_list_remove(&toplevel->unmap.link);
wl_list_remove(&toplevel->link);
free(toplevel);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct ky_toplevel_manager *manager = wl_container_of(listener, manager, server_destroy);
wl_list_remove(&manager->server_destroy.link);
free(manager);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
struct ky_toplevel_manager *manager = wl_container_of(listener, manager, display_destroy);
wl_list_remove(&manager->new_mapped_toplevel.link);
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
}
bool ky_toplevel_manager_create(struct server *server)
{
struct ky_toplevel_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->global = wl_global_create(server->display, &kywc_toplevel_manager_v1_interface, 1,
manager, ky_toplevel_manager_bind);
if (!manager->global) {
kywc_log(KYWC_WARN, "KYWC toplevel manager create failed");
free(manager);
return false;
}
wl_list_init(&manager->resources);
wl_list_init(&manager->toplevels);
manager->event_loop = wl_display_get_event_loop(server->display);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &manager->server_destroy);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &manager->display_destroy);
manager->new_mapped_toplevel.notify = handle_new_mapped_toplevel;
kywc_view_add_new_mapped_listener(&manager->new_mapped_toplevel);
return true;
}
kylin-wayland-compositor/src/view/xdg_popup.c 0000664 0001750 0001750 00000040751 15160461067 020406 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include "effect/action.h"
#include "effect/effect.h"
#include "input/cursor.h"
#include "input/event.h"
#include "output.h"
#include "scene/decoration.h"
#include "scene/xdg_shell.h"
#include "theme.h"
#include "util/color.h"
#include "view_p.h"
#define XDG_POPUP_NO_DECORATION (0)
struct xdg_popup {
struct wlr_xdg_popup *wlr_xdg_popup;
struct ky_scene_tree *popup_tree;
/* may a view/layer tree or popup tree */
struct ky_scene_tree *parent_tree;
/* shell tree xdg-shell or layer-shell */
struct ky_scene_tree *shell_tree;
/* for shadow and blur */
struct ky_scene_decoration *deco;
struct wl_listener destroy;
struct wl_listener new_popup;
struct wl_listener commit;
struct wl_listener reposition;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener theme_update;
struct view *parent_view;
struct wl_listener parent_position;
struct wl_listener parent_unmap;
uint32_t decoration_flags;
bool topmost_popup;
bool topmost_force_enter;
bool use_usable_area;
};
enum xdg_popup_decoration_flags { ROUND_CORNER = 0, BORDER, SHADOW };
static void handle_xdg_popup_destroy(struct wl_listener *listener, void *data)
{
struct xdg_popup *popup = wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->new_popup.link);
wl_list_remove(&popup->commit.link);
wl_list_remove(&popup->reposition.link);
wl_list_remove(&popup->map.link);
wl_list_remove(&popup->unmap.link);
wl_list_remove(&popup->theme_update.link);
wl_list_remove(&popup->parent_position.link);
wl_list_remove(&popup->parent_unmap.link);
/* only need to destroy the topmost popup parent tree,
* popup tree will be destroyed by xdg_surface destroy in scene
*/
if (popup->topmost_popup) {
ky_scene_node_destroy(&popup->parent_tree->node);
}
free(popup);
}
static struct xdg_popup *_xdg_popup_create(struct wlr_xdg_popup *wlr_xdg_popup,
struct ky_scene_tree *parent,
struct ky_scene_tree *shell, bool use_usable_area);
static void popup_handle_new_xdg_popup(struct wl_listener *listener, void *data)
{
struct xdg_popup *popup = wl_container_of(listener, popup, new_popup);
struct wlr_xdg_popup *wlr_popup = data;
_xdg_popup_create(wlr_popup, popup->popup_tree, popup->shell_tree, popup->use_usable_area);
}
static void popup_unconstrain(struct xdg_popup *popup)
{
/* TODO: popup unconstrain output, add input_manager_get_last_seat */
struct output *output = input_current_output(input_manager_get_default_seat());
struct kywc_box *output_box = popup->use_usable_area ? &output->usable_area : &output->geometry;
int lx = 0, ly = 0;
ky_scene_node_coords(&popup->shell_tree->node, &lx, &ly);
struct wlr_box toplevel_space_box = {
.x = output_box->x - lx,
.y = output_box->y - ly,
.width = output_box->width,
.height = output_box->height,
};
wlr_xdg_popup_unconstrain_from_box(popup->wlr_xdg_popup, &toplevel_space_box);
}
static void handle_xdg_popup_commit(struct wl_listener *listener, void *data)
{
struct xdg_popup *popup = wl_container_of(listener, popup, commit);
struct wlr_xdg_surface *xdg_surface = popup->wlr_xdg_popup->base;
if (xdg_surface->initial_commit) {
popup_unconstrain(popup);
}
if (popup->deco) {
struct wlr_box *geo = &xdg_surface->current.geometry;
int width = geo->width, height = geo->height;
if (width == 0 || height == 0) {
width = xdg_surface->surface->current.width;
height = xdg_surface->surface->current.height;
}
ky_scene_decoration_set_surface_size(popup->deco, width, height);
}
}
static void handle_xdg_popup_reposition(struct wl_listener *listener, void *data)
{
struct xdg_popup *popup = wl_container_of(listener, popup, reposition);
popup_unconstrain(popup);
}
static void xdg_popup_update_decoration(struct xdg_popup *xdg_popup, uint32_t flags)
{
struct theme *theme = theme_manager_get_theme();
if ((flags >> ROUND_CORNER) & 0x1) {
int r = theme->menu_radius;
struct ky_scene_buffer *buffer =
ky_scene_buffer_try_from_surface(xdg_popup->wlr_xdg_popup->base->surface);
ky_scene_node_set_radius(&buffer->node, (int[4]){ r, r, r, r });
ky_scene_decoration_set_round_corner_radius(xdg_popup->deco, (int[4]){ r, r, r, r });
}
if ((flags >> BORDER) & 0x1) {
float border_color[4];
color_float_pa(border_color, theme->active_border_color);
ky_scene_decoration_set_margin(xdg_popup->deco, 0, theme->border_width);
ky_scene_decoration_set_margin_color(xdg_popup->deco, (float[4]){ 0, 0, 0, 0 },
border_color);
}
if ((flags >> SHADOW) & 0x1) {
ky_scene_decoration_set_shadow_count(xdg_popup->deco, theme->menu_shadow_color.num_layers);
for (int i = 0; i < theme->menu_shadow_color.num_layers; i++) {
struct theme_shadow_layer *shadow = &theme->menu_shadow_color.layers[i];
float shadow_color[4];
color_float_pa(shadow_color, shadow->color);
ky_scene_decoration_set_shadow(xdg_popup->deco, i, shadow->off_x, shadow->off_y,
shadow->spread, shadow->blur, shadow_color);
}
}
int offset = (flags >> BORDER) & 0x1 ? -theme->border_width : 0;
ky_scene_node_set_position(ky_scene_node_from_decoration(xdg_popup->deco), offset, offset);
}
static void create_popup_decoration(struct xdg_popup *xdg_popup)
{
struct wlr_xdg_surface *xdg_surface = xdg_popup->wlr_xdg_popup->base;
uint32_t flags = ukui_shell_get_surface_decoration_flags(xdg_surface->surface);
if (flags == XDG_POPUP_NO_DECORATION) {
return;
}
xdg_popup->deco = ky_scene_decoration_create(xdg_popup->popup_tree);
theme_manager_add_update_listener(&xdg_popup->theme_update, false);
struct ky_scene_node *node = ky_scene_node_from_decoration(xdg_popup->deco);
ky_scene_node_lower_to_bottom(node);
ky_scene_node_set_input_bypassed(node, true);
struct wlr_box *geo = &xdg_surface->current.geometry;
int width = geo->width, height = geo->height;
if (width == 0 || height == 0) {
width = xdg_surface->surface->current.width;
height = xdg_surface->surface->current.height;
}
ky_scene_decoration_set_surface_size(xdg_popup->deco, width, height);
ky_scene_decoration_set_mask(xdg_popup->deco, DECORATION_MASK_ALL);
xdg_popup->decoration_flags = flags;
xdg_popup_update_decoration(xdg_popup, flags);
}
static void action_effect_options_adjust(enum action_effect_options_step step,
enum effect_action action,
struct action_effect_options *options, void *user_data)
{
struct xdg_popup *popup = user_data;
switch (step) {
case ACTION_EFFECT_OPTIONS_ADD:
/* FADE_TYPE_CENTER */
options->style = 0;
options->alpha = 0.0;
options->width_scale = 1.0;
options->height_scale = 1.0;
bool topmost = popup->topmost_popup;
bool seat = popup->wlr_xdg_popup->seat;
if (action == EFFECT_ACTION_MAP) {
options->duration = 220;
if (topmost && !seat) {
/* tooltips */
options->width_scale = 0.85;
options->height_scale = 0.85;
options->duration = 200;
} else if (!topmost) {
/* sub menu */
options->y_offset = -4;
}
} else {
options->duration = 180;
if (topmost && !seat) {
options->width_scale = 0.85;
options->height_scale = 0.85;
options->duration = 150;
} else if (!topmost) {
options->y_offset = 4;
}
}
struct animation *animation;
if (topmost && seat) {
animation = animation_manager_get(ANIMATION_TYPE_30_2_8_100);
options->animations.alpha = animation;
options->animations.geometry = animation;
} else {
animation = animation_manager_get(ANIMATION_TYPE_EASE);
options->animations.alpha = animation;
options->animations.geometry = animation;
}
options->surface = popup->wlr_xdg_popup->base->surface;
options->scale = options->surface ? ky_scene_surface_get_scale(options->surface) : 1.0f;
break;
case ACTION_EFFECT_OPTIONS_SURFACE:
case ACTION_EFFECT_OPTIONS_CONFIRM:
break;
}
}
static void handle_xdg_popup_map(struct wl_listener *listener, void *data)
{
struct xdg_popup *popup = wl_container_of(listener, popup, map);
struct wlr_seat *seat = popup->wlr_xdg_popup->seat;
/* force enter the popup surface if has grab */
popup->topmost_force_enter = popup->topmost_popup && seat;
if (popup->topmost_force_enter) {
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat);
if (keyboard) {
wlr_seat_keyboard_enter(seat, popup->wlr_xdg_popup->base->surface, keyboard->keycodes,
keyboard->num_keycodes, &keyboard->modifiers);
} else {
wlr_seat_keyboard_enter(seat, popup->wlr_xdg_popup->base->surface, NULL, 0, NULL);
}
}
create_popup_decoration(popup);
struct ky_scene_node *node = &popup->popup_tree->node;
ky_scene_node_set_enabled(node, true);
node_add_action_effect(node, EFFECT_ACTION_MAP, ACTION_EFFECT_FADE,
action_effect_options_adjust, popup);
cursor_rebase_all(false);
}
static void handle_xdg_popup_unmap(struct wl_listener *listener, void *data)
{
struct xdg_popup *popup = wl_container_of(listener, popup, unmap);
struct ky_scene_node *node = &popup->popup_tree->node;
node_add_action_effect(node, EFFECT_ACTION_UNMAP, ACTION_EFFECT_FADE,
action_effect_options_adjust, popup);
ky_scene_node_set_enabled(node, false);
if (popup->deco) {
ky_scene_node_destroy(ky_scene_node_from_decoration(popup->deco));
wl_list_remove(&popup->theme_update.link);
wl_list_init(&popup->theme_update.link);
popup->deco = NULL;
}
if (popup->topmost_force_enter) {
/* end grab before view_activate_topmost */
wlr_seat_keyboard_end_grab(popup->wlr_xdg_popup->seat);
struct view *parent = popup->parent_view;
struct view *activated_view = view_manager_get_activated();
if (activated_view && activated_view != parent) {
return;
}
if (parent && KYWC_VIEW_IS_ACTIVATABLE(&parent->base) &&
KYWC_VIEW_IS_FOCUSABLE(&parent->base)) {
view_do_activate(parent);
view_set_focus(parent, seat_from_wlr_seat(popup->wlr_xdg_popup->seat));
} else {
view_activate_topmost(false);
}
}
}
static void xdg_popup_handle_theme_update(struct wl_listener *listener, void *data)
{
struct xdg_popup *xdg_popup = wl_container_of(listener, xdg_popup, theme_update);
struct theme_update_event *update_event = data;
uint32_t allowed_mask = THEME_UPDATE_MASK_CORNER_RADIUS | THEME_UPDATE_MASK_OPACITY |
THEME_UPDATE_MASK_BORDER_COLOR | THEME_UPDATE_MASK_SHADOW_COLOR;
if (update_event->update_mask & allowed_mask) {
/* force update all items */
xdg_popup_update_decoration(xdg_popup, xdg_popup->decoration_flags);
}
}
static void xdg_popup_handle_parent_position(struct wl_listener *listener, void *data)
{
struct xdg_popup *xdg_popup = wl_container_of(listener, xdg_popup, parent_position);
int lx = xdg_popup->parent_view->base.geometry.x;
int ly = xdg_popup->parent_view->base.geometry.y;
ky_scene_node_set_position(&xdg_popup->parent_tree->node, lx, ly);
}
static void xdg_popup_handle_parent_unmap(struct wl_listener *listener, void *data)
{
struct xdg_popup *xdg_popup = wl_container_of(listener, xdg_popup, parent_unmap);
wl_list_remove(&xdg_popup->parent_position.link);
wl_list_init(&xdg_popup->parent_position.link);
wl_list_remove(&xdg_popup->parent_unmap.link);
wl_list_init(&xdg_popup->parent_unmap.link);
xdg_popup->parent_view = NULL;
}
static struct xdg_popup *_xdg_popup_create(struct wlr_xdg_popup *wlr_xdg_popup,
struct ky_scene_tree *parent,
struct ky_scene_tree *shell, bool use_usable_area)
{
struct xdg_popup *popup = calloc(1, sizeof(*popup));
if (!popup) {
return NULL;
}
popup->wlr_xdg_popup = wlr_xdg_popup;
popup->parent_tree = parent;
popup->shell_tree = shell;
popup->use_usable_area = use_usable_area;
/* add popup surface to parent tree, popup map and unmap is handled in scene */
popup->popup_tree = ky_scene_xdg_surface_create(parent, wlr_xdg_popup->base);
ky_scene_node_set_enabled(&popup->popup_tree->node, wlr_xdg_popup->base->surface->mapped);
popup->destroy.notify = handle_xdg_popup_destroy;
wl_signal_add(&wlr_xdg_popup->events.destroy, &popup->destroy);
popup->reposition.notify = handle_xdg_popup_reposition;
wl_signal_add(&wlr_xdg_popup->events.reposition, &popup->reposition);
popup->new_popup.notify = popup_handle_new_xdg_popup;
wl_signal_add(&wlr_xdg_popup->base->events.new_popup, &popup->new_popup);
popup->commit.notify = handle_xdg_popup_commit;
wl_signal_add(&wlr_xdg_popup->base->surface->events.commit, &popup->commit);
popup->map.notify = handle_xdg_popup_map;
wl_signal_add(&wlr_xdg_popup->base->surface->events.map, &popup->map);
popup->unmap.notify = handle_xdg_popup_unmap;
wl_signal_add(&wlr_xdg_popup->base->surface->events.unmap, &popup->unmap);
popup->theme_update.notify = xdg_popup_handle_theme_update;
wl_list_init(&popup->theme_update.link);
popup->parent_position.notify = xdg_popup_handle_parent_position;
wl_list_init(&popup->parent_position.link);
popup->parent_unmap.notify = xdg_popup_handle_parent_unmap;
wl_list_init(&popup->parent_unmap.link);
return popup;
}
static bool xdg_popup_hover(struct seat *seat, struct ky_scene_node *node, double x, double y,
uint32_t time, bool first, bool hold, void *data)
{
struct wlr_surface *surface = wlr_surface_try_from_node(node);
if (first) {
kywc_log(KYWC_DEBUG, "First hover surface %p (%f %f)", surface, x, y);
}
seat_notify_motion(seat, surface, time, x, y, first);
return false;
}
static void xdg_popup_click(struct seat *seat, struct ky_scene_node *node, uint32_t button,
bool pressed, uint32_t time, enum click_state state, void *data)
{
seat_notify_button(seat, time, button, pressed);
}
static void xdg_popup_leave(struct seat *seat, struct ky_scene_node *node, bool last, void *data)
{
/* so surface will call set_cursor when enter again */
struct wlr_surface *surface = wlr_surface_try_from_node(node);
seat_notify_leave(seat, surface);
}
static struct ky_scene_node *xdg_popup_get_root(void *data)
{
struct xdg_popup *popup = data;
return &popup->parent_tree->node;
}
static const struct input_event_node_impl xdg_popup_event_node_impl = {
.hover = xdg_popup_hover,
.click = xdg_popup_click,
.leave = xdg_popup_leave,
};
void xdg_popup_create(struct wlr_xdg_popup *wlr_xdg_popup, struct ky_scene_tree *shell,
struct view_layer *layer, bool use_usable_area)
{
struct ky_scene_tree *parent = ky_scene_tree_create(layer->tree);
/* get shell layout coord, and set it to parent tree */
int lx, ly;
ky_scene_node_coords(&shell->node, &lx, &ly);
ky_scene_node_set_position(&parent->node, lx, ly);
struct xdg_popup *popup = _xdg_popup_create(wlr_xdg_popup, parent, shell, use_usable_area);
popup->topmost_popup = true;
struct view *parent_view = view_try_from_wlr_surface(wlr_xdg_popup->parent);
popup->parent_view = parent_view;
if (parent_view) {
wl_signal_add(&parent_view->base.events.position, &popup->parent_position);
wl_signal_add(&parent_view->base.events.destroy, &popup->parent_unmap);
}
input_event_node_create(&parent->node, &xdg_popup_event_node_impl, xdg_popup_get_root, NULL,
popup);
}
kylin-wayland-compositor/src/view/kde_plasma_window.c 0000664 0001750 0001750 00000102665 15160461067 022073 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include "plasma-window-management-protocol.h"
#include "theme.h"
#include "view_p.h"
#define PLASMA_WINDOW_MANAGEMENT_VERSION 17
struct kde_plasma_window_management {
struct wl_global *global;
struct wl_list resources;
struct wl_list windows;
struct wl_listener new_mapped_view;
struct wl_listener show_desktop;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
uint32_t window_id_counter;
};
struct kde_plasma_window {
struct wl_list resources;
struct wl_list link;
struct kde_plasma_window_management *management;
bool minimizable, maximizable, fullscreenable;
bool closeable, movable, resizable;
struct kywc_view *kywc_view;
struct wl_listener view_unmap;
struct wl_listener view_title;
struct wl_listener view_app_id;
struct wl_listener view_activate;
struct wl_listener view_minimize;
struct wl_listener view_maximize;
struct wl_listener view_fullscreen;
struct wl_listener view_above;
struct wl_listener view_below;
struct wl_listener view_skip_taskbar;
struct wl_listener view_skip_switcher;
struct wl_listener view_demands_attention;
struct wl_listener view_capabilities;
struct wl_listener view_position;
struct wl_listener view_size;
struct wl_listener view_icon_update;
struct wl_listener view_update_capabilities;
/* The internal window id and uuid */
uint32_t id;
const char *uuid;
/* bitfield of state flags */
uint32_t states;
};
enum state_flag {
STATE_FLAG_ACTIVE = 0,
STATE_FLAG_MINIMIZED,
STATE_FLAG_MAXIMIZED,
STATE_FLAG_FULLSCREEN,
STATE_FLAG_KEEP_ABOVE,
STATE_FLAG_KEEP_BELOW,
STATE_FLAG_ON_ALL_DESKTOPS,
STATE_FLAG_DEMANDS_ATTENTION,
STATE_FLAG_CLOSEABLE,
STATE_FLAG_MINIMIZABLE,
STATE_FLAG_MAXIMIZABLE,
STATE_FLAG_FULLSCREENABLE,
STATE_FLAG_SKIPTASKBAR,
STATE_FLAG_SHADEABLE,
STATE_FLAG_SHADED,
STATE_FLAG_MOVABLE,
STATE_FLAG_RESIZABLE,
STATE_FLAG_VIRTUAL_DESKTOP_CHANGEABLE,
STATE_FLAG_SKIPSWITCHER,
STATE_FLAG_LAST,
};
static void kde_plasma_window_set_state(struct kde_plasma_window *window, enum state_flag flag,
bool state)
{
uint32_t mask = 0;
switch (flag) {
case STATE_FLAG_ACTIVE:
assert(state);
kywc_view_activate(window->kywc_view);
view_set_focus(view_from_kywc_view(window->kywc_view), input_manager_get_default_seat());
break;
case STATE_FLAG_MINIMIZED:
kywc_view_set_minimized(window->kywc_view, state);
break;
case STATE_FLAG_MAXIMIZED:
kywc_view_set_maximized(window->kywc_view, state, NULL);
break;
case STATE_FLAG_FULLSCREEN:
kywc_view_set_fullscreen(window->kywc_view, state, NULL);
break;
case STATE_FLAG_KEEP_ABOVE:
kywc_view_set_kept_above(window->kywc_view, state);
break;
case STATE_FLAG_KEEP_BELOW:
kywc_view_set_kept_below(window->kywc_view, state);
break;
case STATE_FLAG_ON_ALL_DESKTOPS:
break;
case STATE_FLAG_DEMANDS_ATTENTION:
window->kywc_view->demands_attention = state;
break;
case STATE_FLAG_CLOSEABLE:
window->closeable = state;
mask |= KYWC_VIEW_CLOSEABLE;
break;
case STATE_FLAG_MINIMIZABLE:
window->minimizable = state;
mask |= KYWC_VIEW_MINIMIZABLE | KYWC_VIEW_MINIMIZE_BUTTON;
break;
case STATE_FLAG_MAXIMIZABLE:
window->maximizable = state;
mask |= KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_MAXIMIZE_BUTTON;
break;
case STATE_FLAG_FULLSCREENABLE:
window->fullscreenable = state;
mask |= KYWC_VIEW_FULLSCREENABLE;
break;
case STATE_FLAG_SKIPTASKBAR:
window->kywc_view->skip_taskbar = state;
break;
case STATE_FLAG_SHADEABLE:
break;
case STATE_FLAG_SHADED:
break;
case STATE_FLAG_MOVABLE:
window->movable = state;
mask |= KYWC_VIEW_MOVABLE;
break;
case STATE_FLAG_RESIZABLE:
window->resizable = state;
mask |= KYWC_VIEW_RESIZABLE;
break;
case STATE_FLAG_VIRTUAL_DESKTOP_CHANGEABLE:
break;
case STATE_FLAG_SKIPSWITCHER:
window->kywc_view->skip_switcher = state;
break;
case STATE_FLAG_LAST:
break;
}
if (mask != 0) {
view_update_capabilities(view_from_kywc_view(window->kywc_view), mask);
}
}
static void kde_plasma_window_send_state(struct kde_plasma_window *window,
struct wl_resource *resource, bool force);
static void handle_set_state(struct wl_client *client, struct wl_resource *resource, uint32_t flags,
uint32_t state)
{
struct kde_plasma_window *window = wl_resource_get_user_data(resource);
if (!window) {
return;
}
for (int i = 0; i < STATE_FLAG_LAST; i++) {
if ((flags >> i) & 0x1) {
kde_plasma_window_set_state(window, i, (state >> i) & 0x1);
}
}
kde_plasma_window_send_state(window, NULL, false);
}
static void handle_set_virtual_desktop(struct wl_client *client, struct wl_resource *resource,
uint32_t number)
{
// Not implemented yet
}
static void handle_set_minimized_geometry(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *panel, uint32_t x, uint32_t y,
uint32_t width, uint32_t height)
{
// Not implemented yet
}
static void handle_unset_minimized_geometry(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *panel)
{
// Not implemented yet
}
static void handle_close(struct wl_client *client, struct wl_resource *resource)
{
struct kde_plasma_window *window = wl_resource_get_user_data(resource);
if (!window) {
return;
}
kywc_view_close(window->kywc_view);
}
static void handle_request_move(struct wl_client *client, struct wl_resource *resource)
{
// Not implemented yet
}
static void handle_request_resize(struct wl_client *client, struct wl_resource *resource)
{
// Not implemented yet
}
static void handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void handle_get_icon(struct wl_client *client, struct wl_resource *resource, int32_t fd)
{
// Not implemented yet
}
static void handle_request_enter_virtual_desktop(struct wl_client *client,
struct wl_resource *resource, const char *id)
{
// Not implemented yet
}
static void handle_request_enter_new_virtual_desktop(struct wl_client *client,
struct wl_resource *resource)
{
// Not implemented yet
}
static void handle_request_leave_virtual_desktop(struct wl_client *client,
struct wl_resource *resource, const char *id)
{
// Not implemented yet
}
static void handle_request_enter_activity(struct wl_client *client, struct wl_resource *resource,
const char *id)
{
// Not implemented yet
}
static void handle_request_leave_activity(struct wl_client *client, struct wl_resource *resource,
const char *id)
{
// Not implemented yet
}
static void handle_send_to_output(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *output)
{
// Not implemented yet
}
static const struct org_kde_plasma_window_interface kde_plasma_window_impl = {
.set_state = handle_set_state,
.set_virtual_desktop = handle_set_virtual_desktop,
.set_minimized_geometry = handle_set_minimized_geometry,
.unset_minimized_geometry = handle_unset_minimized_geometry,
.close = handle_close,
.request_move = handle_request_move,
.request_resize = handle_request_resize,
.destroy = handle_destroy,
.get_icon = handle_get_icon,
.request_enter_virtual_desktop = handle_request_enter_virtual_desktop,
.request_enter_new_virtual_desktop = handle_request_enter_new_virtual_desktop,
.request_leave_virtual_desktop = handle_request_leave_virtual_desktop,
.request_enter_activity = handle_request_enter_activity,
.request_leave_activity = handle_request_leave_activity,
.send_to_output = handle_send_to_output,
};
static struct kde_plasma_window *
kde_plasma_window_from_id(struct kde_plasma_window_management *management, uint32_t id)
{
struct kde_plasma_window *window;
wl_list_for_each(window, &management->windows, link) {
if (window->id == id) {
return window;
}
}
return NULL;
}
static struct kde_plasma_window *
kde_plasma_window_from_uuid(struct kde_plasma_window_management *management, const char *uuid)
{
struct kde_plasma_window *window;
wl_list_for_each(window, &management->windows, link) {
if (strcmp(window->uuid, uuid) == 0) {
return window;
}
}
return NULL;
}
static void window_handle_resource_destroy(struct wl_resource *resource)
{
wl_resource_set_destructor(resource, NULL);
wl_resource_set_user_data(resource, NULL);
wl_list_remove(wl_resource_get_link(resource));
}
static void window_handle_view_title(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_title);
if (!window->kywc_view->title) {
return;
}
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_title_changed(resource, window->kywc_view->title);
}
}
static void window_handle_view_app_id(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_app_id);
if (!window->kywc_view->app_id) {
return;
}
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_app_id_changed(resource, window->kywc_view->app_id);
}
}
#define set_state(states, prop, state) \
if (prop) { \
states |= ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_##state; \
} else { \
states &= ~ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_##state; \
}
static void window_handle_view_activate(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_activate);
set_state(window->states, window->kywc_view->activated, ACTIVE);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_minimize(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_minimize);
set_state(window->states, window->kywc_view->minimized, MINIMIZED);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_maximize(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_maximize);
set_state(window->states, window->kywc_view->maximized, MAXIMIZED);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_fullscreen(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_fullscreen);
set_state(window->states, window->kywc_view->fullscreen, FULLSCREEN);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_above(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_above);
set_state(window->states, window->kywc_view->kept_above, KEEP_ABOVE);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_below(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_below);
set_state(window->states, window->kywc_view->kept_below, KEEP_BELOW);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_skip_taskbar(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_skip_taskbar);
set_state(window->states, window->kywc_view->skip_taskbar, SKIPTASKBAR);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_skip_switcher(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_skip_switcher);
set_state(window->states, window->kywc_view->skip_switcher, SKIPSWITCHER);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_demands_attention(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_demands_attention);
set_state(window->states, window->kywc_view->demands_attention, DEMANDS_ATTENTION);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_capabilities(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_capabilities);
struct kywc_view_capabilities_event *event = data;
if (event->mask & (KYWC_VIEW_MINIMIZABLE | KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_CLOSEABLE |
KYWC_VIEW_FULLSCREENABLE | KYWC_VIEW_MOVABLE | KYWC_VIEW_RESIZABLE)) {
kde_plasma_window_send_state(window, NULL, false);
}
}
static void window_handle_view_position(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_position);
struct kywc_view *view = window->kywc_view;
int32_t x = view->geometry.x - view->margin.off_x;
int32_t y = view->geometry.y - view->margin.off_y;
uint32_t width = view->geometry.width + view->margin.off_width;
uint32_t height = view->geometry.height + view->margin.off_height;
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_geometry(resource, x, y, width, height);
}
}
static void window_handle_view_size(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_size);
struct kywc_view *view = window->kywc_view;
int32_t x = view->geometry.x - view->margin.off_x;
int32_t y = view->geometry.y - view->margin.off_y;
uint32_t width = view->geometry.width + view->margin.off_width;
uint32_t height = view->geometry.height + view->margin.off_height;
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_geometry(resource, x, y, width, height);
}
}
static void kde_plasma_window_send_state(struct kde_plasma_window *window,
struct wl_resource *resource, bool force)
{
struct kywc_view *kywc_view = window->kywc_view;
uint32_t states = window->states;
set_state(states, kywc_view->activated, ACTIVE);
set_state(states, kywc_view->minimized, MINIMIZED);
set_state(states, kywc_view->maximized, MAXIMIZED);
set_state(states, kywc_view->fullscreen, FULLSCREEN);
set_state(states, kywc_view->kept_above, KEEP_ABOVE);
set_state(states, kywc_view->kept_below, KEEP_BELOW);
// ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS
set_state(states, kywc_view->demands_attention, DEMANDS_ATTENTION);
set_state(states, KYWC_VIEW_IS_CLOSEABLE(kywc_view), CLOSEABLE);
set_state(states, KYWC_VIEW_IS_MINIMIZABLE(kywc_view), MINIMIZABLE);
set_state(states, KYWC_VIEW_IS_MAXIMIZABLE(kywc_view), MAXIMIZABLE);
set_state(states, KYWC_VIEW_IS_FULLSCREENABLE(kywc_view), FULLSCREENABLE);
set_state(states, kywc_view->skip_taskbar, SKIPTASKBAR);
// ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE
// ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED
set_state(states, KYWC_VIEW_IS_MOVABLE(kywc_view), MOVABLE);
set_state(states, KYWC_VIEW_IS_RESIZABLE(kywc_view), RESIZABLE);
// ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE
set_state(states, kywc_view->skip_switcher, SKIPSWITCHER);
if (force || states != window->states) {
window->states = states;
if (resource) {
org_kde_plasma_window_send_state_changed(resource, window->states);
} else {
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_state_changed(resource, window->states);
}
}
}
}
#undef set_state
static void kde_plasma_window_add_resource(struct kde_plasma_window *window,
struct wl_resource *management_resource, uint32_t id)
{
struct wl_client *client = wl_resource_get_client(management_resource);
uint32_t version = wl_resource_get_version(management_resource);
struct wl_resource *resource =
wl_resource_create(client, &org_kde_plasma_window_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_list_insert(&window->resources, wl_resource_get_link(resource));
wl_resource_set_implementation(resource, &kde_plasma_window_impl, window,
window_handle_resource_destroy);
/* send states */
kde_plasma_window_send_state(window, resource, true);
struct kywc_view *kywc_view = window->kywc_view;
struct view *view = view_from_kywc_view(kywc_view);
if (kywc_view->title) {
org_kde_plasma_window_send_title_changed(resource, kywc_view->title);
}
if (kywc_view->app_id) {
org_kde_plasma_window_send_app_id_changed(resource, kywc_view->app_id);
}
if (view->pid) {
org_kde_plasma_window_send_pid_changed(resource, view->pid);
}
int32_t x = kywc_view->geometry.x - kywc_view->margin.off_x;
int32_t y = kywc_view->geometry.y - kywc_view->margin.off_y;
uint32_t width = kywc_view->geometry.width + kywc_view->margin.off_width;
uint32_t height = kywc_view->geometry.height + kywc_view->margin.off_height;
org_kde_plasma_window_send_geometry(resource, x, y, width, height);
// org_kde_plasma_window_send_parent_window
// org_kde_plasma_window_send_virtual_desktop_changed
// org_kde_plasma_window_send_virtual_desktop_entered
// org_kde_plasma_window_send_virtual_desktop_left
if (!theme_icon_is_fallback(view->icon)) {
const char *icon_name = theme_icon_get_name(view->icon);
org_kde_plasma_window_send_themed_icon_name_changed(resource, icon_name);
}
// org_kde_plasma_window_send_icon_changed
// org_kde_plasma_window_send_application_menu
// org_kde_plasma_window_send_activity_entered
// org_kde_plasma_window_send_activity_left
// org_kde_plasma_window_send_resource_name_changed
org_kde_plasma_window_send_initial_state(resource);
}
/* deprecated */
static void handle_get_window(struct wl_client *client, struct wl_resource *management_resource,
uint32_t id, uint32_t internal_window_id)
{
struct kde_plasma_window_management *management =
wl_resource_get_user_data(management_resource);
struct kde_plasma_window *window = kde_plasma_window_from_id(management, internal_window_id);
if (!window) {
return;
}
kde_plasma_window_add_resource(window, management_resource, id);
}
static void handle_get_window_by_uuid(struct wl_client *client,
struct wl_resource *management_resource, uint32_t id,
const char *internal_window_uuid)
{
struct kde_plasma_window_management *management =
wl_resource_get_user_data(management_resource);
struct kde_plasma_window *window =
kde_plasma_window_from_uuid(management, internal_window_uuid);
if (!window) {
return;
}
kde_plasma_window_add_resource(window, management_resource, id);
}
static void handle_show_desktop(struct wl_client *client, struct wl_resource *resource,
uint32_t state)
{
view_manager_show_desktop(state == ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED, true);
}
static void handle_get_stacking_order(struct wl_client *client,
struct wl_resource *management_resource, uint32_t id)
{
uint32_t version = wl_resource_get_version(management_resource);
struct wl_resource *resource =
wl_resource_create(client, &org_kde_plasma_stacking_order_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
// TODO: org_kde_plasma_stacking_order_send_window and org_kde_plasma_stacking_order_send_done
wl_resource_destroy(resource);
}
static const struct org_kde_plasma_window_management_interface kde_plasma_window_management_impl = {
.show_desktop = handle_show_desktop,
.get_window = handle_get_window,
.get_window_by_uuid = handle_get_window_by_uuid,
.get_stacking_order = handle_get_stacking_order,
};
static void management_handle_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
}
static void kde_plasma_window_management_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id)
{
struct kde_plasma_window_management *management = data;
struct wl_resource *resource =
wl_resource_create(client, &org_kde_plasma_window_management_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_list_insert(&management->resources, wl_resource_get_link(resource));
wl_resource_set_implementation(resource, &kde_plasma_window_management_impl, management,
management_handle_resource_destroy);
org_kde_plasma_window_management_send_show_desktop_changed(
resource, view_manager_get_show_desktop()
? ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED
: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED);
struct kde_plasma_window *window;
wl_list_for_each(window, &management->windows, link) {
if (version >= ORG_KDE_PLASMA_WINDOW_MANAGEMENT_WINDOW_WITH_UUID_SINCE_VERSION) {
org_kde_plasma_window_management_send_window_with_uuid(resource, window->id,
window->uuid);
} else {
org_kde_plasma_window_management_send_window(resource, window->id);
}
}
if (version >= ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STACKING_ORDER_CHANGED_2_SINCE_VERSION) {
org_kde_plasma_window_management_send_stacking_order_changed_2(resource);
}
}
static void window_handle_view_unmap(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_unmap);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
org_kde_plasma_window_send_unmapped(resource);
}
wl_list_remove(&window->view_unmap.link);
wl_list_remove(&window->view_title.link);
wl_list_remove(&window->view_app_id.link);
wl_list_remove(&window->view_activate.link);
wl_list_remove(&window->view_minimize.link);
wl_list_remove(&window->view_maximize.link);
wl_list_remove(&window->view_fullscreen.link);
wl_list_remove(&window->view_above.link);
wl_list_remove(&window->view_below.link);
wl_list_remove(&window->view_skip_taskbar.link);
wl_list_remove(&window->view_skip_switcher.link);
wl_list_remove(&window->view_demands_attention.link);
wl_list_remove(&window->view_capabilities.link);
wl_list_remove(&window->view_position.link);
wl_list_remove(&window->view_size.link);
wl_list_remove(&window->view_icon_update.link);
wl_list_remove(&window->view_update_capabilities.link);
wl_list_remove(&window->link);
struct wl_resource *tmp;
wl_resource_for_each_safe(resource, tmp, &window->resources) {
window_handle_resource_destroy(resource);
}
free(window);
}
static void window_handle_view_icon_update(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_icon_update);
struct view *view = view_from_kywc_view(window->kywc_view);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
if (!theme_icon_is_fallback(view->icon)) {
const char *icon_name = theme_icon_get_name(view->icon);
org_kde_plasma_window_send_themed_icon_name_changed(resource, icon_name);
}
}
}
static void window_handle_view_update_capabilities(struct wl_listener *listener, void *data)
{
struct kde_plasma_window *window = wl_container_of(listener, window, view_update_capabilities);
struct view_update_capabilities_event *event = data;
if (event->mask & KYWC_VIEW_MINIMIZABLE) {
if (!window->minimizable) {
event->state &= ~KYWC_VIEW_MINIMIZABLE;
}
}
if (event->mask & KYWC_VIEW_MAXIMIZABLE) {
if (!window->maximizable) {
event->state &= ~KYWC_VIEW_MAXIMIZABLE;
}
}
if (event->mask & KYWC_VIEW_CLOSEABLE) {
if (!window->closeable) {
event->state &= ~KYWC_VIEW_CLOSEABLE;
}
}
if (event->mask & KYWC_VIEW_FULLSCREENABLE) {
if (!window->fullscreenable) {
event->state &= ~KYWC_VIEW_FULLSCREENABLE;
}
}
if (event->mask & KYWC_VIEW_MOVABLE) {
if (!window->movable) {
event->state &= ~KYWC_VIEW_MOVABLE;
}
}
if (event->mask & KYWC_VIEW_RESIZABLE) {
if (!window->resizable) {
event->state &= ~KYWC_VIEW_RESIZABLE;
}
}
if (event->mask & KYWC_VIEW_MINIMIZE_BUTTON) {
if (!window->minimizable) {
event->state &= ~KYWC_VIEW_MINIMIZE_BUTTON;
}
}
if (event->mask & KYWC_VIEW_MAXIMIZE_BUTTON) {
if (!window->maximizable) {
event->state &= ~KYWC_VIEW_MAXIMIZE_BUTTON;
}
}
}
static void handle_new_mapped_view(struct wl_listener *listener, void *data)
{
struct kde_plasma_window_management *management =
wl_container_of(listener, management, new_mapped_view);
struct kywc_view *kywc_view = data;
struct kde_plasma_window *window = calloc(1, sizeof(*window));
if (!window) {
return;
}
window->management = management;
wl_list_init(&window->resources);
wl_list_insert(&management->windows, &window->link);
window->id = management->window_id_counter++;
window->uuid = kywc_view->uuid;
window->minimizable = window->maximizable = window->fullscreenable = window->closeable =
window->movable = window->resizable = true;
window->kywc_view = kywc_view;
window->view_unmap.notify = window_handle_view_unmap;
wl_signal_add(&kywc_view->events.unmap, &window->view_unmap);
window->view_title.notify = window_handle_view_title;
wl_signal_add(&kywc_view->events.title, &window->view_title);
window->view_app_id.notify = window_handle_view_app_id;
wl_signal_add(&kywc_view->events.app_id, &window->view_app_id);
window->view_activate.notify = window_handle_view_activate;
wl_signal_add(&kywc_view->events.activate, &window->view_activate);
window->view_minimize.notify = window_handle_view_minimize;
wl_signal_add(&kywc_view->events.minimize, &window->view_minimize);
window->view_maximize.notify = window_handle_view_maximize;
wl_signal_add(&kywc_view->events.maximize, &window->view_maximize);
window->view_fullscreen.notify = window_handle_view_fullscreen;
wl_signal_add(&kywc_view->events.fullscreen, &window->view_fullscreen);
window->view_above.notify = window_handle_view_above;
wl_signal_add(&kywc_view->events.above, &window->view_above);
window->view_below.notify = window_handle_view_below;
wl_signal_add(&kywc_view->events.below, &window->view_below);
window->view_skip_taskbar.notify = window_handle_view_skip_taskbar;
wl_signal_add(&kywc_view->events.skip_taskbar, &window->view_skip_taskbar);
window->view_skip_switcher.notify = window_handle_view_skip_switcher;
wl_signal_add(&kywc_view->events.skip_switcher, &window->view_skip_switcher);
window->view_demands_attention.notify = window_handle_view_demands_attention;
wl_signal_add(&kywc_view->events.demands_attention, &window->view_demands_attention);
window->view_capabilities.notify = window_handle_view_capabilities;
wl_signal_add(&kywc_view->events.capabilities, &window->view_capabilities);
window->view_position.notify = window_handle_view_position;
wl_signal_add(&kywc_view->events.position, &window->view_position);
window->view_size.notify = window_handle_view_size;
wl_signal_add(&kywc_view->events.size, &window->view_size);
struct view *view = view_from_kywc_view(kywc_view);
window->view_icon_update.notify = window_handle_view_icon_update;
wl_signal_add(&view->events.icon_update, &window->view_icon_update);
window->view_update_capabilities.notify = window_handle_view_update_capabilities;
view_add_update_capabilities_listener(view, &window->view_update_capabilities);
struct wl_resource *resource;
wl_resource_for_each(resource, &management->resources) {
// org_kde_plasma_window_management_send_window(resource, window->id);
org_kde_plasma_window_management_send_window_with_uuid(resource, window->id, window->uuid);
}
}
static void handle_shown_desktop(struct wl_listener *listener, void *data)
{
struct kde_plasma_window_management *management =
wl_container_of(listener, management, show_desktop);
bool enabled = view_manager_get_show_desktop();
struct wl_resource *resource;
wl_resource_for_each(resource, &management->resources) {
org_kde_plasma_window_management_send_show_desktop_changed(
resource, enabled ? ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED
: ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED);
}
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
struct kde_plasma_window_management *management =
wl_container_of(listener, management, display_destroy);
wl_list_remove(&management->display_destroy.link);
wl_list_remove(&management->new_mapped_view.link);
wl_list_remove(&management->show_desktop.link);
wl_global_destroy(management->global);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct kde_plasma_window_management *management =
wl_container_of(listener, management, server_destroy);
wl_list_remove(&management->server_destroy.link);
free(management);
}
bool kde_plasma_window_management_create(struct server *server)
{
struct kde_plasma_window_management *management = calloc(1, sizeof(*management));
if (!management) {
return false;
}
management->global = wl_global_create(
server->display, &org_kde_plasma_window_management_interface,
PLASMA_WINDOW_MANAGEMENT_VERSION, management, kde_plasma_window_management_bind);
if (!management->global) {
kywc_log(KYWC_WARN, "Kde plasma window management create failed");
free(management);
return false;
}
wl_list_init(&management->windows);
wl_list_init(&management->resources);
management->window_id_counter = 0;
management->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &management->server_destroy);
management->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &management->display_destroy);
management->new_mapped_view.notify = handle_new_mapped_view;
kywc_view_add_new_mapped_listener(&management->new_mapped_view);
management->show_desktop.notify = handle_shown_desktop;
view_manager_add_show_desktop_listener(&management->show_desktop);
return true;
}
kylin-wayland-compositor/src/view/modal.c 0000664 0001750 0001750 00000023573 15160461067 017500 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include "effect/node_transform.h"
#include "effect/shake_view.h"
#include "input/event.h"
#include "render/renderer.h"
#include "scene/render.h"
#include "scene/surface.h"
#include "theme.h"
#include "util/time.h"
#include "view_p.h"
static float modal_color[4] = { 18.0 / 255, 18.0 / 255, 18.0 / 255, 51.0 / 255 };
struct modal_manager {
struct wl_list modals;
struct wl_listener new_mapped;
struct wl_listener server_destroy;
bool hardware_renderer;
};
struct modal {
struct wl_list link;
struct modal_manager *modal_manager;
struct view *view;
struct ky_scene_rect *modal_box;
struct kywc_box geo;
struct wl_listener modal;
struct wl_listener view_unmap;
struct wl_listener parent_unmap;
struct wl_listener parent_size;
};
static bool modal_hover(struct seat *seat, struct ky_scene_node *node, double x, double y,
uint32_t time, bool first, bool hold, void *data)
{
return false;
}
static void modal_leave(struct seat *seat, struct ky_scene_node *node, bool last, void *data) {}
static void modal_click(struct seat *seat, struct ky_scene_node *node, uint32_t button,
bool pressed, uint32_t time, enum click_state state, void *data)
{
if (!pressed) {
return;
}
struct modal *modal = data;
struct view *view = view_find_descendant_modal(modal->view);
view = view ? view : modal->view;
/* active current view */
kywc_view_activate(&view->base);
view_set_focus(view, seat);
if (modal->modal_manager->hardware_renderer) {
view_add_shake_effect(view);
}
}
static struct ky_scene_node *modal_get_root(void *data)
{
struct modal *modal = data;
return &modal->modal_box->node;
}
static const struct input_event_node_impl modal_impl = {
.hover = modal_hover,
.leave = modal_leave,
.click = modal_click,
};
static void modal_add_rect_opacity_transform(struct ky_scene_node *node, int duration,
struct kywc_box geo, float start_alpha,
float end_alpha, int64_t start_time,
enum animation_type alpha_type)
{
struct transform_options options = {
.start_time = start_time,
.duration = duration,
.start = { .geometry = geo, .alpha = start_alpha },
.end = { .geometry = geo, .alpha = end_alpha },
.animations = { .alpha = animation_manager_get(alpha_type) },
};
node_add_transform_effect(node, &options);
}
static void modal_deactive(struct modal *modal)
{
wl_list_remove(&modal->parent_unmap.link);
wl_list_remove(&modal->parent_size.link);
wl_list_init(&modal->parent_unmap.link);
wl_list_init(&modal->parent_size.link);
if (modal->modal_box) {
modal_add_rect_opacity_transform(&modal->modal_box->node, 200, modal->geo, 1.0, 0,
current_time_msec(), ANIMATION_TYPE_EASE);
ky_scene_node_destroy(&modal->modal_box->node);
}
}
static void handle_parent_unmap(struct wl_listener *listener, void *data)
{
struct modal *modal = wl_container_of(listener, modal, parent_unmap);
kywc_view_set_modal(&modal->view->base, false);
}
static void modal_box_set_round_corner(struct ky_scene_rect *modal_box, struct view *view);
static void handle_parent_size(struct wl_listener *listener, void *data)
{
struct modal *modal = wl_container_of(listener, modal, parent_size);
struct kywc_view *parent = &modal->view->parent->base;
struct kywc_box geo = {
.x = -parent->margin.off_x,
.y = -parent->margin.off_y,
.width = parent->geometry.width + parent->margin.off_width,
.height = parent->geometry.height + parent->margin.off_height,
};
struct kywc_box geometry = { parent->geometry.x + geo.x, parent->geometry.y + geo.y, geo.width,
geo.height };
modal->geo = geometry;
ky_scene_rect_set_size(modal->modal_box, geo.width, geo.height);
ky_scene_node_set_position(&modal->modal_box->node, geo.x, geo.y);
modal_box_set_round_corner(modal->modal_box, modal->view->parent);
}
static void modal_box_set_round_corner(struct ky_scene_rect *modal_box, struct view *view)
{
struct ky_scene_buffer *buffer = ky_scene_buffer_try_from_surface(view->surface);
if (!buffer) {
return;
}
int radius[4] = { 0 };
memcpy(radius, buffer->node.radius, sizeof(radius));
/* set top corner if has ssd title */
struct kywc_view *kywc_view = &view->base;
if (kywc_view->ssd & KYWC_SSD_TITLE) {
bool need_corner = !kywc_view->maximized && !kywc_view->fullscreen && !kywc_view->tiled;
struct theme *theme = theme_manager_get_theme();
radius[KY_SCENE_ROUND_CORNER_RT] = need_corner ? theme->window_radius : 0;
radius[KY_SCENE_ROUND_CORNER_LT] = need_corner ? theme->window_radius : 0;
}
ky_scene_node_set_radius(&modal_box->node, radius);
}
static void modal_box_render(struct ky_scene_node *node, int lx, int ly,
struct ky_scene_render_target *target)
{
/* When the modal disappears, obtain the rect thumbnail and render it */
if (!node->enabled || (!node->has_effect && target->options & KY_SCENE_RENDER_DISABLE_EFFECT)) {
return;
}
struct ky_scene_rect *rect = ky_scene_rect_from_node(node);
if (rect->color[3] == 0) {
return;
}
bool render_with_visibility = !(target->options & KY_SCENE_RENDER_DISABLE_VISIBILITY);
if (render_with_visibility && !pixman_region32_not_empty(&node->visible_region) &&
!pixman_region32_not_empty(&node->extend_render_region)) {
return;
}
ky_scene_rect_render(node, (struct kywc_box){ lx, ly, rect->width, rect->height }, rect->color,
render_with_visibility, target);
}
static void modal_active(struct modal *modal)
{
if (!modal->view->parent || !modal->view->parent->surface) {
return;
}
struct kywc_view *parent = &modal->view->parent->base;
struct kywc_box geo = {
.x = -parent->margin.off_x,
.y = -parent->margin.off_y,
.width = parent->geometry.width + parent->margin.off_width,
.height = parent->geometry.height + parent->margin.off_height,
};
modal->modal_box =
ky_scene_rect_create(modal->view->parent->tree, geo.width, geo.height, modal_color);
if (!modal->modal_box) {
return;
}
modal->modal_box->node.impl.render = modal_box_render;
ky_scene_node_raise_to_top(&modal->modal_box->node);
ky_scene_node_set_position(&modal->modal_box->node, geo.x, geo.y);
modal_box_set_round_corner(modal->modal_box, modal->view->parent);
struct kywc_box geometry = { parent->geometry.x + geo.x, parent->geometry.y + geo.y, geo.width,
geo.height };
modal->geo = geometry;
modal_add_rect_opacity_transform(&modal->modal_box->node, 300, modal->geo, 0, 1.0,
current_time_msec(), ANIMATION_TYPE_EASE);
input_event_node_create(&modal->modal_box->node, &modal_impl, modal_get_root, NULL, modal);
view_add_parent_workspace(modal->view);
wl_signal_add(&parent->events.unmap, &modal->parent_unmap);
wl_signal_add(&parent->events.size, &modal->parent_size);
}
static void handle_modal_view_unmap(struct wl_listener *listener, void *data)
{
struct modal *modal = wl_container_of(listener, modal, view_unmap);
if (modal->view->base.modal) {
modal_deactive(modal);
}
wl_list_remove(&modal->modal.link);
wl_list_remove(&modal->parent_unmap.link);
wl_list_remove(&modal->parent_size.link);
wl_list_remove(&modal->view_unmap.link);
wl_list_remove(&modal->link);
free(modal);
}
static void handle_view_modal(struct wl_listener *listener, void *data)
{
struct modal *modal = wl_container_of(listener, modal, modal);
if (modal->view->base.modal) {
modal_active(modal);
} else {
modal_deactive(modal);
}
}
static void handle_new_mapped_view(struct wl_listener *listener, void *data)
{
struct modal_manager *modal_manager = wl_container_of(listener, modal_manager, new_mapped);
struct kywc_view *kywc_view = data;
struct modal *modal = calloc(1, sizeof(*modal));
if (!modal) {
return;
}
modal->modal_manager = modal_manager;
wl_list_insert(&modal_manager->modals, &modal->link);
modal->modal.notify = handle_view_modal;
wl_signal_add(&kywc_view->events.modal, &modal->modal);
modal->view_unmap.notify = handle_modal_view_unmap;
wl_signal_add(&kywc_view->events.unmap, &modal->view_unmap);
modal->parent_unmap.notify = handle_parent_unmap;
wl_list_init(&modal->parent_unmap.link);
modal->parent_size.notify = handle_parent_size;
wl_list_init(&modal->parent_size.link);
modal->view = view_from_kywc_view(kywc_view);
if (kywc_view->modal) {
modal_active(modal);
}
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct modal_manager *modal_manager = wl_container_of(listener, modal_manager, server_destroy);
wl_list_remove(&modal_manager->server_destroy.link);
wl_list_remove(&modal_manager->new_mapped.link);
free(modal_manager);
}
bool modal_manager_create(struct server *server)
{
struct modal_manager *modal_manager = calloc(1, sizeof(*modal_manager));
if (!modal_manager) {
return false;
}
wl_list_init(&modal_manager->modals);
modal_manager->hardware_renderer = !ky_renderer_is_software(server->renderer);
modal_manager->new_mapped.notify = handle_new_mapped_view;
kywc_view_add_new_mapped_listener(&modal_manager->new_mapped);
modal_manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &modal_manager->server_destroy);
return true;
}
kylin-wayland-compositor/src/view/workspace.c 0000664 0001750 0001750 00000052773 15160461067 020406 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include "effect/translation.h"
#include "input/cursor.h"
#include "nls.h"
#include "server.h"
#include "util/macros.h"
#include "util/string.h"
#include "view/workspace.h"
#include "view_p.h"
#define TRANSLATION_THRESHOLD 0.35
struct workspace_manager {
struct view_manager *view_manager;
struct workspace_translation *translation; // for worskspace translation effect
/* current activated workspace */
struct workspace *current;
struct workspace **workspaces;
struct {
struct wl_signal new_workspace;
} events;
struct wl_listener display_destroy;
struct wl_listener server_ready;
struct wl_listener server_destroy;
uint32_t count, rows, columns;
};
static struct workspace_manager *workspace_manager = NULL;
static struct shortcut {
char *keybind;
char *desc;
int switch_workspace;
} shortcuts[] = {
{ "Ctrl+Alt+Left:no", "switch to left workspace", DIRECTION_LEFT },
{ "Ctrl+Alt+Right:no", "switch to right workspace", DIRECTION_RIGHT },
{ "Ctrl+F1:no", "switch to workspace 0", 4 },
{ "Ctrl+F2:no", "switch to workspace 1", 5 },
{ "Ctrl+F3:no", "switch to workspace 2", 6 },
{ "Ctrl+F4:no", "switch to workspace 3", 7 },
{ "Win+ctrl+left:no", "switch to left workspace", DIRECTION_LEFT },
{ "Win+ctrl+Right:no", "switch to right workspace", DIRECTION_RIGHT },
};
static void workspace_switch_to(int switch_workspace)
{
int32_t current = workspace_manager->current->position;
int32_t last = workspace_manager->count - 1;
int32_t pending = current;
if (switch_workspace == DIRECTION_LEFT) {
pending = current - 1;
} else if (switch_workspace == DIRECTION_RIGHT) {
pending = current + 1;
} else {
/* add ctrl+f1...f4 to workspace */
pending = switch_workspace - 4;
if (pending == current || pending > last) {
return;
}
switch_workspace = pending > current ? DIRECTION_RIGHT : DIRECTION_LEFT;
}
struct workspace *pending_workspace =
pending < 0 || pending > last ? NULL : workspace_manager->workspaces[pending];
if (!workspace_add_automatic_translation_effect(workspace_manager->workspaces[current],
pending_workspace, switch_workspace)) {
workspace_activate(pending_workspace);
}
}
static void shortcut_action(struct key_binding *binding, void *data)
{
struct shortcut *shortcut = data;
workspace_switch_to(shortcut->switch_workspace);
}
static struct gesture {
enum gesture_type type;
enum gesture_stage stage;
uint8_t fingers;
uint32_t devices;
uint32_t directions;
uint32_t edges;
uint32_t follow_direction;
double follow_threshold;
char *desc;
} gestures[] = {
{
GESTURE_TYPE_SWIPE,
GESTURE_STAGE_BEFORE,
4,
GESTURE_DEVICE_TOUCHPAD,
GESTURE_DIRECTION_NONE,
GESTURE_EDGE_NONE,
GESTURE_DIRECTION_LEFT,
0.0,
"switch workspace tridge before",
},
{
GESTURE_TYPE_SWIPE,
GESTURE_STAGE_BEFORE,
4,
GESTURE_DEVICE_TOUCHPAD,
GESTURE_DIRECTION_NONE,
GESTURE_EDGE_NONE,
GESTURE_DIRECTION_RIGHT,
0.0,
"switch workspace tridge before",
},
{
GESTURE_TYPE_SWIPE,
GESTURE_STAGE_TRIGGER,
4,
GESTURE_DEVICE_TOUCHPAD,
GESTURE_DIRECTION_LEFT,
GESTURE_EDGE_NONE,
GESTURE_DIRECTION_NONE,
0.0,
"switch workspace tridge",
},
{
GESTURE_TYPE_SWIPE,
GESTURE_STAGE_TRIGGER,
4,
GESTURE_DEVICE_TOUCHPAD,
GESTURE_DIRECTION_RIGHT,
GESTURE_EDGE_NONE,
GESTURE_DIRECTION_NONE,
0.0,
"switch workspace tridge",
},
{
GESTURE_TYPE_SWIPE,
GESTURE_STAGE_AFTER,
4,
GESTURE_DEVICE_TOUCHPAD,
GESTURE_DIRECTION_LEFT | GESTURE_DIRECTION_RIGHT,
GESTURE_EDGE_NONE,
GESTURE_DIRECTION_LEFT,
0.0,
"switch workspace left follow finger",
},
{
GESTURE_TYPE_SWIPE,
GESTURE_STAGE_AFTER,
4,
GESTURE_DEVICE_TOUCHPAD,
GESTURE_DIRECTION_LEFT | GESTURE_DIRECTION_RIGHT,
GESTURE_EDGE_NONE,
GESTURE_DIRECTION_RIGHT,
0.0,
"switch workspace right follow finger",
},
{
GESTURE_TYPE_SWIPE,
GESTURE_STAGE_STOP,
4,
GESTURE_DEVICE_TOUCHPAD,
GESTURE_DIRECTION_NONE,
GESTURE_EDGE_NONE,
GESTURE_DIRECTION_NONE,
0.0,
"stop switch workspace follow finger",
},
};
static void gestures_action(struct gesture_binding *binding, void *data, double dx, double dy)
{
struct gesture *gesture = data;
/* the opposite direction of the gesture for the drag effect */
enum direction direct = DIRECTION_UP;
if (gesture->follow_direction == GESTURE_DIRECTION_LEFT) {
direct = DIRECTION_RIGHT;
} else if (gesture->follow_direction == GESTURE_DIRECTION_RIGHT) {
direct = DIRECTION_LEFT;
}
float percent = -dx / 300;
if (gesture->stage == GESTURE_STAGE_BEFORE) {
if (!workspace_manager->translation) {
workspace_manager->translation =
workspace_create_manual_translation_effect(workspace_manager->current);
if (!workspace_manager->translation) {
return;
}
kywc_log(KYWC_DEBUG,
"Workspace manual translation direct: %d, dx = %f, dy = %f, percent = %f",
direct, dx, dy, percent);
}
workspace_translation_manual(workspace_manager->translation, direct, percent);
} else if (gesture->stage == GESTURE_STAGE_TRIGGER) {
if (workspace_manager->translation &&
(direct == DIRECTION_RIGHT || direct == DIRECTION_LEFT)) {
workspace_translation_manual(workspace_manager->translation, direct, percent);
} else if (!workspace_manager->translation) {
if (gesture->directions == GESTURE_DIRECTION_LEFT) {
direct = DIRECTION_RIGHT;
} else if (gesture->directions == GESTURE_DIRECTION_RIGHT) {
direct = DIRECTION_LEFT;
}
workspace_switch_to(direct);
kywc_log(KYWC_DEBUG,
"Workspace auto translation direct: %d, dx = %f, dy = %f, percent = %f",
direct, dx, dy, percent);
}
} else if (gesture->stage == GESTURE_STAGE_AFTER && workspace_manager->translation &&
(direct == DIRECTION_RIGHT || direct == DIRECTION_LEFT)) {
workspace_translation_manual(workspace_manager->translation, direct, percent);
} else if (gesture->stage == GESTURE_STAGE_STOP && workspace_manager->translation) {
struct workspace *last_show =
workspace_translation_destroy(workspace_manager->translation, TRANSLATION_THRESHOLD);
if (last_show) {
workspace_activate(last_show);
}
workspace_manager->translation = NULL;
}
}
static void workspace_register_shortcut(void)
{
for (size_t i = 0; i < ARRAY_SIZE(shortcuts); i++) {
struct shortcut *shortcut = &shortcuts[i];
struct key_binding *binding = kywc_key_binding_create(shortcut->keybind, shortcut->desc);
if (!binding) {
continue;
}
if (!kywc_key_binding_register(binding, KEY_BINDING_TYPE_SWITCH_WORKSPACE, shortcut_action,
shortcut)) {
kywc_key_binding_destroy(binding);
continue;
}
}
for (size_t i = 0; i < ARRAY_SIZE(gestures); i++) {
struct gesture *gesture = &gestures[i];
struct gesture_binding *binding = kywc_gesture_binding_create(
gesture->type, gesture->stage, gesture->devices, gesture->directions, gesture->edges,
gesture->fingers, gesture->follow_direction, gesture->follow_threshold, gesture->desc);
if (!binding) {
continue;
}
if (!kywc_gesture_binding_register(binding, gestures_action, gesture)) {
kywc_gesture_binding_destroy(binding);
continue;
}
}
}
static void workspace_manager_update_count(uint32_t count)
{
uint32_t column = count / workspace_manager->rows;
if (count % workspace_manager->rows > 0) {
column++;
}
workspace_manager->count = count;
workspace_manager->columns = column;
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
assert(wl_list_empty(&workspace_manager->events.new_workspace.listener_list));
wl_list_remove(&workspace_manager->server_destroy.link);
wl_list_remove(&workspace_manager->server_ready.link);
free(workspace_manager->workspaces);
free(workspace_manager);
workspace_manager = NULL;
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&workspace_manager->display_destroy.link);
workspace_manager->current = NULL;
/* workspace_destroy will update count */
while (workspace_manager->count) {
workspace_destroy(workspace_manager->workspaces[0]);
}
}
static void handle_server_ready(struct wl_listener *listener, void *data)
{
/* create the default workspace and activate it */
workspace_activate(
workspace_create(workspace_manager->view_manager->state.workspace_names[0], 0));
/* create workspaces according to configuration */
for (uint32_t i = 1; i < workspace_manager->view_manager->state.num_workspaces; i++) {
workspace_create(workspace_manager->view_manager->state.workspace_names[i], i);
}
}
bool workspace_manager_create(struct view_manager *view_manager)
{
workspace_manager = calloc(1, sizeof(*workspace_manager));
if (!workspace_manager) {
return false;
}
workspace_manager->view_manager = view_manager;
workspace_manager->workspaces = calloc(MAX_WORKSPACES, sizeof(struct workspace *));
workspace_manager->rows = 2;
wl_signal_init(&workspace_manager->events.new_workspace);
workspace_manager->server_ready.notify = handle_server_ready;
wl_signal_add(&view_manager->server->events.ready, &workspace_manager->server_ready);
workspace_manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(view_manager->server, &workspace_manager->server_destroy);
workspace_manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(view_manager->server->display,
&workspace_manager->display_destroy);
ky_workspace_manager_create(view_manager->server);
/* kde-plasma-virtual-desktop support */
kde_virtual_desktop_management_create(view_manager->server);
workspace_register_shortcut();
return true;
}
void workspace_manager_add_new_listener(struct wl_listener *listener)
{
wl_signal_add(&workspace_manager->events.new_workspace, listener);
}
void workspace_manager_set_rows(uint32_t rows)
{
if (rows == workspace_manager->rows) {
return;
}
workspace_manager->rows = rows;
/* update columns */
workspace_manager_update_count(workspace_manager->count);
}
uint32_t workspace_manager_get_rows(void)
{
return workspace_manager->rows;
}
struct workspace *workspace_manager_get_current(void)
{
return workspace_manager->current;
}
uint32_t workspace_manager_get_count(void)
{
return workspace_manager->count;
}
void workspace_manager_for_each_workspace(workspace_iterator_func_t iterator, void *data)
{
for (uint32_t i = 0; i < workspace_manager->count; i++) {
if (iterator(workspace_manager->workspaces[i], data)) {
break;
}
}
}
static void workspace_set_enabled(struct workspace *workspace, bool enabled)
{
for (int i = 0; i < 3; i++) {
ky_scene_node_set_enabled(&workspace->layers[i].tree->node, enabled);
}
}
void workspace_update_name(struct workspace *workspace, const char *name)
{
if (workspace->name && name && strcmp(workspace->name, name) == 0) {
return;
}
free((void *)workspace->name);
workspace->name = STRING_VALID(name)
? strdup(name)
: string_create("%s %d", tr("Desktop"), workspace->position + 1);
workspace->has_custom_name = STRING_VALID(name);
wl_signal_emit_mutable(&workspace->events.name, NULL);
}
/* auto add views in all workspaces */
static void workspace_auto_add_views(struct workspace *workspace)
{
if (!workspace_manager->view_manager->server->ready) {
return;
}
struct workspace *first_workspace = workspace_manager->workspaces[0];
struct view_proxy *view_proxy;
wl_list_for_each(view_proxy, &first_workspace->view_proxies, workspace_link) {
struct view *view = view_proxy->view;
if (view->base.sticky) {
view_add_workspace(view, workspace);
}
}
}
struct workspace *workspace_create(const char *name, uint32_t position)
{
/* too many workspaces, reject it */
if (workspace_manager->count == MAX_WORKSPACES) {
return NULL;
}
struct workspace *workspace = calloc(1, sizeof(*workspace));
if (!workspace) {
return NULL;
};
if (position > workspace_manager->count) {
position = workspace_manager->count;
}
wl_list_init(&workspace->view_proxies);
wl_signal_init(&workspace->events.name);
wl_signal_init(&workspace->events.position);
wl_signal_init(&workspace->events.activate);
wl_signal_init(&workspace->events.destroy);
wl_signal_init(&workspace->events.view_enter);
wl_signal_init(&workspace->events.view_leave);
/* create 3 tree per workspace and disabled default */
struct view_layer *layers = workspace_manager->view_manager->layers;
workspace->layers[0].layer = LAYER_BELOW;
workspace->layers[0].tree = ky_scene_tree_create(layers[LAYER_BELOW].tree);
workspace->layers[0].tree->node.role.type = KY_SCENE_ROLE_WORKSPACE;
workspace->layers[0].tree->node.role.data = &workspace->layers[0];
workspace->layers[1].layer = LAYER_NORMAL;
workspace->layers[1].tree = ky_scene_tree_create(layers[LAYER_NORMAL].tree);
workspace->layers[1].tree->node.role.type = KY_SCENE_ROLE_WORKSPACE;
workspace->layers[1].tree->node.role.data = &workspace->layers[1];
workspace->layers[2].layer = LAYER_ABOVE;
workspace->layers[2].tree = ky_scene_tree_create(layers[LAYER_ABOVE].tree);
workspace->layers[2].tree->node.role.type = KY_SCENE_ROLE_WORKSPACE;
workspace->layers[2].tree->node.role.data = &workspace->layers[2];
workspace_set_enabled(workspace, false);
/* insert to workspace manager workspaces */
for (uint32_t i = workspace_manager->count; i > position; i--) {
workspace_manager->workspaces[i] = workspace_manager->workspaces[i - 1];
workspace_manager->workspaces[i]->position = i;
}
workspace->position = position;
workspace_manager->workspaces[position] = workspace;
workspace_manager_update_count(workspace_manager->count + 1);
workspace->uuid = kywc_identifier_uuid_generate();
workspace_update_name(workspace, name);
wl_signal_emit_mutable(&workspace_manager->events.new_workspace, workspace);
/* after new_workspace */
workspace_auto_add_views(workspace);
return workspace;
}
static void fix_workspace(struct workspace *workspace)
{
/* fixup workspace position */
for (uint32_t i = workspace->position; i < workspace_manager->count - 1; i++) {
workspace_manager->workspaces[i] = workspace_manager->workspaces[i + 1];
workspace_manager->workspaces[i]->position = i;
wl_signal_emit_mutable(&workspace_manager->workspaces[i]->events.position, NULL);
if (!workspace_manager->workspaces[i]->has_custom_name) {
workspace_update_name(workspace_manager->workspaces[i], NULL);
}
}
workspace_manager_update_count(workspace_manager->count - 1);
if (workspace_manager->count == 0) {
return;
}
/* fixup activated workspace */
struct workspace *activate_workspace =
workspace_manager->workspaces[workspace->position ? workspace->position - 1 : 0];
if (workspace_manager->current == workspace) {
workspace_activate(activate_workspace);
}
/* move all views to activate workspace */
struct view_proxy *view_proxy, *tmp;
wl_list_for_each_safe(view_proxy, tmp, &workspace->view_proxies, workspace_link) {
struct view_proxy *new_proxy = view_add_workspace(view_proxy->view, activate_workspace);
if (new_proxy) {
view_set_current_proxy(view_proxy->view, new_proxy);
}
view_proxy_destroy(view_proxy);
}
}
void workspace_destroy(struct workspace *workspace)
{
kywc_log(KYWC_INFO, "Workspace %s destroy", workspace->name);
fix_workspace(workspace);
wl_signal_emit_mutable(&workspace->events.destroy, NULL);
assert(wl_list_empty(&workspace->events.view_enter.listener_list));
assert(wl_list_empty(&workspace->events.view_leave.listener_list));
assert(wl_list_empty(&workspace->events.name.listener_list));
assert(wl_list_empty(&workspace->events.position.listener_list));
assert(wl_list_empty(&workspace->events.activate.listener_list));
assert(wl_list_empty(&workspace->events.destroy.listener_list));
/* destroy trees, trees must be empty */
for (int i = 0; i < 3; i++) {
ky_scene_node_destroy(&workspace->layers[i].tree->node);
}
free((void *)workspace->uuid);
free((void *)workspace->name);
free(workspace);
}
static void workspace_set_activated(struct workspace *workspace, bool activated)
{
if (workspace->activated == activated) {
return;
}
workspace_set_enabled(workspace, activated);
/* reparent view_tree which in multi workspace */
struct view_proxy *view_proxy;
wl_list_for_each(view_proxy, &workspace->view_proxies, workspace_link) {
if (view_proxy->view->current_proxy != view_proxy) {
view_set_current_proxy(view_proxy->view, view_proxy);
}
}
workspace->activated = activated;
wl_signal_emit_mutable(&workspace->events.activate, NULL);
}
void workspace_activate(struct workspace *workspace)
{
struct workspace *old = workspace_manager->current;
if (!workspace || old == workspace) {
return;
}
if (old) {
workspace_set_activated(old, false);
}
/* disable show desktop when switching between workspaces */
view_manager_show_desktop(false, true);
workspace_manager->current = workspace;
workspace_set_activated(workspace, true);
/* auto activate topmost enabled view */
view_activate_topmost(true);
cursor_rebase_all(false);
kywc_log(KYWC_INFO, "Workspace %s(%d) is activated", workspace->name, workspace->position);
}
void workspace_activate_with_effect(struct workspace *workspace)
{
struct workspace *current = workspace_manager->current;
enum direction direction =
current->position < workspace->position ? DIRECTION_RIGHT : DIRECTION_LEFT;
if (!workspace_add_automatic_translation_effect(current, workspace, direction)) {
workspace_activate(workspace);
}
}
struct view_layer *workspace_layer(struct workspace *workspace, enum layer layer)
{
switch (layer) {
case LAYER_BELOW:
return &workspace->layers[0];
case LAYER_NORMAL:
return &workspace->layers[1];
case LAYER_ABOVE:
return &workspace->layers[2];
default:
return NULL;
}
}
struct workspace *workspace_by_position(uint32_t position)
{
if (position >= workspace_manager->count) {
return NULL;
}
return workspace_manager->workspaces[position];
}
struct workspace *workspace_by_uuid(const char *uuid)
{
struct workspace *workspace;
for (uint32_t i = 0; i < workspace_manager->count; i++) {
workspace = workspace_manager->workspaces[i];
if (strcmp(workspace->uuid, uuid) == 0) {
return workspace;
}
}
return NULL;
}
void workspace_set_position(struct workspace *workspace, uint32_t position)
{
/* insert to last if position is too bigger */
if (position >= workspace_manager->count) {
position = workspace_manager->count - 1;
}
if (position == workspace->position) {
return;
}
/* move workspaces */
for (uint32_t i = workspace->position; i > position; i--) {
workspace_manager->workspaces[i] = workspace_manager->workspaces[i - 1];
workspace_manager->workspaces[i]->position = i;
wl_signal_emit_mutable(&workspace_manager->workspaces[i]->events.position, NULL);
if (!workspace_manager->workspaces[i]->has_custom_name) {
workspace_update_name(workspace_manager->workspaces[i], NULL);
}
}
for (uint32_t i = workspace->position; i < position; i++) {
workspace_manager->workspaces[i] = workspace_manager->workspaces[i + 1];
workspace_manager->workspaces[i]->position = i;
wl_signal_emit_mutable(&workspace_manager->workspaces[i]->events.position, NULL);
if (!workspace_manager->workspaces[i]->has_custom_name) {
workspace_update_name(workspace_manager->workspaces[i], NULL);
}
}
workspace->position = position;
workspace_manager->workspaces[position] = workspace;
wl_signal_emit_mutable(&workspace->events.position, NULL);
if (!workspace->has_custom_name) {
workspace_update_name(workspace, NULL);
}
}
kylin-wayland-compositor/src/view/wlr_foreign_toplevel.c 0000664 0001750 0001750 00000030600 15160460057 022616 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include "input/seat.h"
#include "output.h"
#include "scene/surface.h"
#include "view_p.h"
struct wlr_foreign_manager {
struct wlr_foreign_toplevel_manager_v1 *manager;
struct wl_listener new_mapped_view;
struct wl_listener destroy;
};
struct wlr_foreign {
struct wlr_foreign_manager *manager;
struct wlr_foreign_toplevel_handle_v1 *toplevel_handle;
struct kywc_view *toplevel_view;
struct wlr_surface *surface;
struct wl_listener output_enter;
struct wl_listener output_leave;
struct wl_listener view_unmap;
struct wl_listener view_maximize;
struct wl_listener view_minimize;
struct wl_listener view_activate;
struct wl_listener view_fullscreen;
struct wl_listener view_title;
struct wl_listener view_app_id;
struct wl_listener request_maximize;
struct wl_listener request_minimize;
struct wl_listener request_activate;
struct wl_listener request_fullscreen;
struct wl_listener request_close;
struct wl_listener set_rectangle;
struct wl_listener destroy;
};
static void handle_request_maximize(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, request_maximize);
struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data;
kywc_view_set_maximized(foreign->toplevel_view, event->maximized, NULL);
}
static void handle_request_minimize(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, request_minimize);
struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data;
kywc_view_set_minimized(foreign->toplevel_view, event->minimized);
}
static void handle_request_activate(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, request_activate);
struct wlr_foreign_toplevel_handle_v1_activated_event *event = data;
struct kywc_view *view = foreign->toplevel_view;
kywc_view_activate(view);
view_set_focus(view_from_kywc_view(view), seat_from_wlr_seat(event->seat));
}
static void handle_request_fullscreen(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, request_fullscreen);
struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data;
struct kywc_view *view = foreign->toplevel_view;
struct kywc_output *kywc_output = NULL;
if (event->output) {
struct output *output = output_from_wlr_output(event->output);
if (output && !output->base.destroying && output->base.state.enabled) {
kywc_output = &output->base;
}
}
kywc_view_set_fullscreen(view, event->fullscreen, kywc_output);
}
static void handle_request_close(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, request_close);
struct kywc_view *view = foreign->toplevel_view;
kywc_view_close(view);
}
static void handle_set_rectangle(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, request_close);
struct wlr_foreign_toplevel_handle_v1_set_rectangle_event *event = data;
kywc_log(KYWC_DEBUG, "Set surface %p rectangle (%d, %d), %d x %d", event->surface, event->x,
event->y, event->width, event->height);
}
static void handle_foreign_destroy(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, destroy);
wl_list_remove(&foreign->output_enter.link);
wl_list_remove(&foreign->output_leave.link);
wl_list_remove(&foreign->view_maximize.link);
wl_list_remove(&foreign->view_minimize.link);
wl_list_remove(&foreign->view_activate.link);
wl_list_remove(&foreign->view_fullscreen.link);
wl_list_remove(&foreign->view_title.link);
wl_list_remove(&foreign->view_app_id.link);
wl_list_remove(&foreign->request_maximize.link);
wl_list_remove(&foreign->request_minimize.link);
wl_list_remove(&foreign->request_activate.link);
wl_list_remove(&foreign->request_fullscreen.link);
wl_list_remove(&foreign->request_close.link);
wl_list_remove(&foreign->set_rectangle.link);
wl_list_remove(&foreign->destroy.link);
}
static void handle_view_maximize(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, view_maximize);
struct wlr_foreign_toplevel_handle_v1 *toplevel = foreign->toplevel_handle;
struct kywc_view *view = foreign->toplevel_view;
wlr_foreign_toplevel_handle_v1_set_maximized(toplevel, view->maximized);
}
static void handle_view_minimize(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, view_minimize);
struct wlr_foreign_toplevel_handle_v1 *toplevel = foreign->toplevel_handle;
struct kywc_view *view = foreign->toplevel_view;
wlr_foreign_toplevel_handle_v1_set_minimized(toplevel, view->minimized);
}
static void handle_view_activate(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, view_activate);
struct wlr_foreign_toplevel_handle_v1 *toplevel = foreign->toplevel_handle;
struct kywc_view *view = foreign->toplevel_view;
wlr_foreign_toplevel_handle_v1_set_activated(toplevel, view->activated);
}
static void handle_view_fullscreen(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, view_fullscreen);
struct wlr_foreign_toplevel_handle_v1 *toplevel = foreign->toplevel_handle;
struct kywc_view *view = foreign->toplevel_view;
wlr_foreign_toplevel_handle_v1_set_fullscreen(toplevel, view->fullscreen);
}
static void handle_view_title(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, view_title);
struct wlr_foreign_toplevel_handle_v1 *toplevel = foreign->toplevel_handle;
struct kywc_view *view = foreign->toplevel_view;
if (view->title) {
wlr_foreign_toplevel_handle_v1_set_title(toplevel, view->title);
}
}
static void handle_view_app_id(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, view_app_id);
struct wlr_foreign_toplevel_handle_v1 *toplevel = foreign->toplevel_handle;
struct kywc_view *view = foreign->toplevel_view;
if (view->app_id) {
wlr_foreign_toplevel_handle_v1_set_app_id(toplevel, view->app_id);
}
}
static void handle_view_unmap(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, view_unmap);
if (foreign->toplevel_handle) {
wlr_foreign_toplevel_handle_v1_destroy(foreign->toplevel_handle);
foreign->toplevel_handle = NULL;
}
wl_list_remove(&foreign->view_unmap.link);
free(foreign);
}
static void handle_output_enter(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, output_enter);
struct ky_scene_output *output = data;
wlr_foreign_toplevel_handle_v1_output_enter(foreign->toplevel_handle, output->output);
}
static void handle_output_leave(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = wl_container_of(listener, foreign, output_leave);
struct ky_scene_output *output = data;
wlr_foreign_toplevel_handle_v1_output_leave(foreign->toplevel_handle, output->output);
}
static void handle_new_mapped_view(struct wl_listener *listener, void *data)
{
struct wlr_foreign *foreign = calloc(1, sizeof(*foreign));
if (!foreign) {
return;
}
struct wlr_foreign_manager *manager = wl_container_of(listener, manager, new_mapped_view);
foreign->manager = manager;
foreign->toplevel_handle = wlr_foreign_toplevel_handle_v1_create(manager->manager);
if (!foreign->toplevel_handle) {
free(foreign);
return;
}
foreign->request_maximize.notify = handle_request_maximize;
wl_signal_add(&foreign->toplevel_handle->events.request_maximize, &foreign->request_maximize);
foreign->request_minimize.notify = handle_request_minimize;
wl_signal_add(&foreign->toplevel_handle->events.request_minimize, &foreign->request_minimize);
foreign->request_activate.notify = handle_request_activate;
wl_signal_add(&foreign->toplevel_handle->events.request_activate, &foreign->request_activate);
foreign->request_fullscreen.notify = handle_request_fullscreen;
wl_signal_add(&foreign->toplevel_handle->events.request_fullscreen,
&foreign->request_fullscreen);
foreign->request_close.notify = handle_request_close;
wl_signal_add(&foreign->toplevel_handle->events.request_close, &foreign->request_close);
foreign->set_rectangle.notify = handle_set_rectangle;
wl_signal_add(&foreign->toplevel_handle->events.set_rectangle, &foreign->set_rectangle);
foreign->destroy.notify = handle_foreign_destroy;
wl_signal_add(&foreign->toplevel_handle->events.destroy, &foreign->destroy);
struct kywc_view *view = data;
foreign->toplevel_view = view;
foreign->view_unmap.notify = handle_view_unmap;
wl_signal_add(&view->events.unmap, &foreign->view_unmap);
foreign->view_maximize.notify = handle_view_maximize;
wl_signal_add(&view->events.maximize, &foreign->view_maximize);
foreign->view_minimize.notify = handle_view_minimize;
wl_signal_add(&view->events.minimize, &foreign->view_minimize);
foreign->view_activate.notify = handle_view_activate;
wl_signal_add(&view->events.activate, &foreign->view_activate);
foreign->view_fullscreen.notify = handle_view_fullscreen;
wl_signal_add(&view->events.fullscreen, &foreign->view_fullscreen);
foreign->view_title.notify = handle_view_title;
wl_signal_add(&view->events.title, &foreign->view_title);
foreign->view_app_id.notify = handle_view_app_id;
wl_signal_add(&view->events.app_id, &foreign->view_app_id);
struct wlr_surface *surface = view_from_kywc_view(view)->surface;
foreign->surface = surface;
struct ky_scene_buffer *buffer = ky_scene_buffer_try_from_surface(surface);
foreign->output_enter.notify = handle_output_enter;
wl_signal_add(&buffer->events.output_enter, &foreign->output_enter);
foreign->output_leave.notify = handle_output_leave;
wl_signal_add(&buffer->events.output_enter, &foreign->output_leave);
/* set toplevel state */
struct wlr_foreign_toplevel_handle_v1 *toplevel = foreign->toplevel_handle;
struct wlr_surface_output *surface_output;
wl_list_for_each(surface_output, &surface->current_outputs, link) {
wlr_foreign_toplevel_handle_v1_output_enter(foreign->toplevel_handle,
surface_output->output);
}
// TODO: foreign_toplevel_handle_from_view
// wlr_foreign_toplevel_handle_v1_set_parent(toplevel);
if (view->title) {
wlr_foreign_toplevel_handle_v1_set_title(toplevel, view->title);
}
if (view->app_id) {
wlr_foreign_toplevel_handle_v1_set_app_id(toplevel, view->app_id);
}
wlr_foreign_toplevel_handle_v1_set_maximized(toplevel, view->maximized);
wlr_foreign_toplevel_handle_v1_set_minimized(toplevel, view->minimized);
wlr_foreign_toplevel_handle_v1_set_fullscreen(toplevel, view->fullscreen);
wlr_foreign_toplevel_handle_v1_set_activated(toplevel, view->activated);
}
static void handle_destroy(struct wl_listener *listener, void *data)
{
struct wlr_foreign_manager *manager = wl_container_of(listener, manager, destroy);
wl_list_remove(&manager->destroy.link);
wl_list_remove(&manager->new_mapped_view.link);
free(manager);
}
bool wlr_foreign_toplevel_manager_create(struct server *server)
{
struct wlr_foreign_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->manager = wlr_foreign_toplevel_manager_v1_create(server->display);
if (!manager->manager) {
free(manager);
return false;
}
/* monitor new mapped view */
manager->new_mapped_view.notify = handle_new_mapped_view;
kywc_view_add_new_mapped_listener(&manager->new_mapped_view);
manager->destroy.notify = handle_destroy;
wl_signal_add(&manager->manager->events.destroy, &manager->destroy);
return true;
}
kylin-wayland-compositor/src/view/view.c 0000664 0001750 0001750 00000260033 15160461067 017350 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "effect/action.h"
#include "effect/magic_lamp.h"
#include "effect/scale.h"
#include "effect/slide.h"
#include "input/cursor.h"
#include "input/seat.h"
#include "output.h"
#include "scene/surface.h"
#include "server.h"
#include "theme.h"
#include "util/macros.h"
#include "view/workspace.h"
#include "view_p.h"
#include "xwayland.h"
static struct view_manager *view_manager = NULL;
static struct view *view_find_fullscreen_ancestor(struct view *view);
struct view *view_manager_get_activated(void)
{
return view_manager->activated.view;
}
void view_manager_add_activate_view_listener(struct wl_listener *listener)
{
wl_signal_add(&view_manager->events.activate_view, listener);
}
void view_manager_show_tile_assist(struct view *view, struct seat *seat,
struct kywc_output *kywc_output)
{
if (view_manager->impl.show_tile_assist) {
view_manager->impl.show_tile_assist(view, seat, kywc_output);
}
}
struct view_layer *view_manager_get_layer(enum layer layer, bool in_workspace)
{
if (!in_workspace) {
return &view_manager->layers[layer];
}
switch (layer) {
case LAYER_BELOW:
case LAYER_NORMAL:
case LAYER_ABOVE:
return workspace_layer(workspace_manager_get_current(), layer);
default:
return &view_manager->layers[layer];
}
}
struct view_layer *view_manager_get_layer_by_node(struct ky_scene_node *node, bool in_workspace)
{
struct ky_scene_tree *tree = node->parent;
while (tree && (!in_workspace || tree->node.role.type != KY_SCENE_ROLE_WORKSPACE) &&
tree->node.role.type != KY_SCENE_ROLE_LAYER) {
tree = tree->node.parent;
}
return tree->node.role.data;
}
static void view_update_output(struct view *view, struct kywc_output *output)
{
/* use fallback output if no valid output */
if (!output) {
output = output_manager_get_fallback();
}
if (view->output == output) {
return;
}
view->output = output;
wl_list_remove(&view->output_destroy.link);
wl_signal_add(&output->events.destroy, &view->output_destroy);
wl_signal_emit_mutable(&view->events.output, NULL);
}
static void view_handle_output_destroy(struct wl_listener *listener, void *data)
{
struct view *view = wl_container_of(listener, view, output_destroy);
view_update_output(view, NULL);
}
static void view_handle_outputs_update(struct wl_listener *listener, void *data)
{
struct view *view = wl_container_of(listener, view, outputs_update);
struct ky_scene_outputs_update_event *event = data;
/* skip update if the primary is empty, will clear when output destroy */
if (!event->primary) {
return;
}
view_update_output(view, &output_from_wlr_output(event->primary->output)->base);
}
void view_fix_geometry(struct view *view, struct kywc_box *geo, struct kywc_box *src_box,
struct kywc_box *dst_box)
{
struct kywc_view *kywc_view = &view->base;
/* actual view geometry with margin */
int x = geo->x - kywc_view->margin.off_x;
int y = geo->y - kywc_view->margin.off_y;
int w = geo->width + kywc_view->margin.off_width;
int h = geo->height + kywc_view->margin.off_height;
if (w > dst_box->width) {
geo->width = dst_box->width - kywc_view->margin.off_width;
geo->x = dst_box->x + kywc_view->margin.off_x;
} else if (src_box->x == x) {
geo->x = dst_box->x + kywc_view->margin.off_x;
} else if (src_box->x + src_box->width == x + w) {
geo->x = dst_box->x + dst_box->width - w + kywc_view->margin.off_x;
} else {
double frac_x = (double)dst_box->width / src_box->width;
geo->x = round(MAX(x - src_box->x, 0) * frac_x) + dst_box->x + kywc_view->margin.off_x;
}
if (h > dst_box->height) {
geo->height = dst_box->height - kywc_view->margin.off_height;
geo->y = dst_box->y + kywc_view->margin.off_y;
} else if (src_box->y == y) {
geo->y = dst_box->y + kywc_view->margin.off_y;
} else if (src_box->y + src_box->height == y + h) {
geo->y = dst_box->y + dst_box->height - h + kywc_view->margin.off_y;
} else {
double frac_y = (double)dst_box->height / src_box->height;
geo->y = round(MAX(y - src_box->y, 0) * frac_y) + dst_box->y + kywc_view->margin.off_y;
}
}
static void view_fix_tiled_geometry(struct view *view, struct kywc_box *geo,
struct kywc_output *kywc_output)
{
if (view_manager->impl.fix_geometry) {
view_manager->impl.fix_geometry(view, geo, kywc_output);
}
}
void view_move_to_output(struct view *view, struct kywc_box *src_box, struct kywc_box *preferred,
struct kywc_output *kywc_output)
{
struct kywc_view *kywc_view = &view->base;
struct output *dst = output_from_kywc_output(kywc_output);
struct kywc_box *dst_box = &dst->usable_area;
/* default to view output usable area */
if (src_box == NULL) {
src_box = &output_from_kywc_output(view->output)->usable_area;
}
if (kywc_view->fullscreen) {
view_fix_geometry(view, &view->saved.geometry, src_box, dst_box);
view_do_resize(view, &dst->geometry);
return;
}
if (kywc_view->maximized) {
struct kywc_box geo;
view_get_tiled_geometry(view, &geo, kywc_output, KYWC_TILE_ALL);
view_fix_geometry(view, &view->saved.geometry, src_box, dst_box);
view_do_resize(view, &geo);
return;
}
if (kywc_view->tiled) {
struct kywc_box geo;
view_fix_tiled_geometry(view, &geo, kywc_output);
view_fix_geometry(view, &view->saved.geometry, src_box, dst_box);
view_do_resize(view, &geo);
return;
}
if (!KYWC_VIEW_IS_MOVABLE(kywc_view)) {
return;
}
struct kywc_box geo = view_action_change_size(view->pending.configure_action)
? view->pending.configure_geometry
: kywc_view->geometry;
if (kywc_box_not_empty(preferred)) {
geo.width = preferred->width;
geo.height = preferred->height;
}
view_fix_geometry(view, &geo, src_box, dst_box);
/* resize to dst */
view_do_resize(view, &geo);
}
void sub_view_move_to_output(struct view *view, struct kywc_output *kywc_output)
{
struct view *child;
wl_list_for_each(child, &view->children, parent_link) {
sub_view_move_to_output(child, kywc_output);
view_move_to_output(child, NULL, NULL, kywc_output);
}
}
static void view_handle_update_capabilities(struct wl_listener *listener, void *data);
void view_init(struct view *view, const struct view_impl *impl, void *data)
{
struct kywc_view *kywc_view = &view->base;
wl_signal_init(&kywc_view->events.premap);
wl_signal_init(&kywc_view->events.map);
wl_signal_init(&kywc_view->events.unmap);
wl_signal_init(&kywc_view->events.destroy);
wl_signal_init(&kywc_view->events.activate);
wl_signal_init(&kywc_view->events.maximize);
wl_signal_init(&kywc_view->events.minimize);
wl_signal_init(&kywc_view->events.fullscreen);
wl_signal_init(&kywc_view->events.tile);
wl_signal_init(&kywc_view->events.above);
wl_signal_init(&kywc_view->events.below);
wl_signal_init(&kywc_view->events.sticky);
wl_signal_init(&kywc_view->events.skip_taskbar);
wl_signal_init(&kywc_view->events.skip_switcher);
wl_signal_init(&kywc_view->events.demands_attention);
wl_signal_init(&kywc_view->events.modal);
wl_signal_init(&kywc_view->events.capabilities);
wl_signal_init(&kywc_view->events.title);
wl_signal_init(&kywc_view->events.app_id);
wl_signal_init(&kywc_view->events.position);
wl_signal_init(&kywc_view->events.size);
wl_signal_init(&kywc_view->events.decoration);
wl_list_init(&view->children);
wl_list_init(&view->parent_link);
wl_list_init(&view->view_proxies);
wl_signal_init(&view->events.parent);
wl_signal_init(&view->events.workspace);
wl_signal_init(&view->events.workspace_enter);
wl_signal_init(&view->events.workspace_leave);
wl_signal_init(&view->events.output);
wl_signal_init(&view->events.icon_update);
wl_signal_init(&view->events.position);
wl_signal_init(&view->events.update_capabilities);
view->impl = impl;
view->data = data;
kywc_view->uuid = kywc_identifier_uuid_generate();
kywc_view->focused_seat = input_manager_get_default_seat();
wl_list_insert(&view_manager->views, &view->link);
/* create view tree and disable it */
struct view_layer *layer = view_manager_get_layer(LAYER_NORMAL, true);
view->tree = ky_scene_tree_create(layer->tree);
ky_scene_node_set_enabled(&view->tree->node, false);
struct output *output = input_current_output(input_manager_get_default_seat());
view->output = output ? &output->base : output_manager_get_fallback();
view->output_destroy.notify = view_handle_output_destroy;
wl_signal_add(&view->output->events.destroy, &view->output_destroy);
view->outputs_update.notify = view_handle_outputs_update;
wl_list_init(&view->outputs_update.link);
kywc_view->role = KYWC_VIEW_ROLE_UNDEF;
view_set_role(view, KYWC_VIEW_ROLE_NORMAL);
view->update_capabilities.notify = view_handle_update_capabilities;
view_add_update_capabilities_listener(view, &view->update_capabilities);
wl_signal_emit_mutable(&view_manager->events.new_view, kywc_view);
}
void view_get_tiled_geometry(struct view *view, struct kywc_box *geometry,
struct kywc_output *kywc_output, enum kywc_tile tile)
{
if (!tile || tile > KYWC_TILE_BOTTOM_RIGHT) {
struct output *output = output_from_kywc_output(kywc_output);
struct kywc_view *kywc_view = &view->base;
int32_t min_width = kywc_view->min_width + kywc_view->margin.off_width;
int32_t min_height = kywc_view->min_height + kywc_view->margin.off_height;
struct kywc_box *usable = &output->usable_area;
int32_t half_width = usable->width / 2;
int32_t half_height = usable->height / 2;
bool use_min_width = half_width < min_width;
bool use_min_height = half_height < min_height;
if (tile == KYWC_TILE_NONE) {
*geometry = view->saved.geometry;
} else if (tile == KYWC_TILE_CENTER) {
geometry->x = use_min_width
? usable->x + half_width - min_width / 2 + kywc_view->margin.off_x
: usable->x + usable->width / 4 + kywc_view->margin.off_x;
geometry->y = use_min_height
? usable->y + half_height - min_height / 2 + kywc_view->margin.off_y
: usable->y + usable->height / 4 + kywc_view->margin.off_y;
geometry->width = use_min_width ? min_width - kywc_view->margin.off_width
: half_width - kywc_view->margin.off_width;
geometry->height = use_min_height ? min_height - kywc_view->margin.off_height
: half_height - kywc_view->margin.off_height;
} else if (tile == KYWC_TILE_ALL) {
geometry->x = usable->x + kywc_view->margin.off_x;
geometry->y = usable->y + kywc_view->margin.off_y;
geometry->width = usable->width - kywc_view->margin.off_width;
geometry->height = usable->height - kywc_view->margin.off_height;
}
return;
}
if (view_manager->impl.get_tiled_geometry) {
view_manager->impl.get_tiled_geometry(view, geometry, kywc_output, tile);
return;
}
}
struct wlr_buffer *view_get_icon_buffer(struct view *view, float scale)
{
struct theme *theme = theme_manager_get_theme();
struct wlr_buffer *buf = NULL;
struct wlr_buffer *theme_buf = theme_icon_get_buffer(view->icon, theme->icon_size, scale);
if (view->icon_name) {
buf = theme_buf;
}
if (!buf && view->impl->get_icon_buffer) {
buf = view->impl->get_icon_buffer(view, theme->icon_size, scale);
}
if (!buf) {
buf = theme_buf;
}
return buf;
}
/* set surface round corner by ssd type, tiled, fullscreen and maximized state */
void view_update_round_corner(struct view *view)
{
struct kywc_view *kywc_view = &view->base;
if (!kywc_view->mapped) {
return;
}
struct ky_scene_buffer *buffer = ky_scene_buffer_try_from_surface(view->surface);
if (!buffer) {
return;
}
int radius[4] = { 0 };
if (!kywc_view->has_round_corner) {
ky_scene_node_set_radius(&buffer->node, radius);
return;
}
/* don't draw top round corner if ssd has title */
struct theme *theme = theme_manager_get_theme();
bool need_corner = !kywc_view->maximized && !kywc_view->fullscreen && !kywc_view->tiled;
if (!view_manager->state.csd_round_corner) {
need_corner &= kywc_view->ssd != KYWC_SSD_NONE;
}
bool need_top_corner = need_corner && !(kywc_view->ssd & KYWC_SSD_TITLE);
radius[KY_SCENE_ROUND_CORNER_RB] = need_corner ? theme->window_radius : 0;
radius[KY_SCENE_ROUND_CORNER_RT] = need_top_corner ? theme->window_radius : 0;
radius[KY_SCENE_ROUND_CORNER_LB] = need_corner ? theme->window_radius : 0;
radius[KY_SCENE_ROUND_CORNER_LT] = need_top_corner ? theme->window_radius : 0;
ky_scene_node_set_radius(&buffer->node, radius);
}
void view_set_icon(struct view *view, bool buffer_changed)
{
struct theme *theme = theme_manager_get_theme();
const char *name = view->base.app_id;
bool need_update = false;
if (view->icon_name) {
name = view->icon_name;
} else if (buffer_changed) {
struct wlr_buffer *buf =
view->impl->get_icon_buffer(view, theme->icon_size, view->output->state.scale);
/* set icon fallback if has any icon buffer */
if (buf) {
name = NULL;
}
}
struct icon *old = view->icon;
view->icon = theme_icon_from_app_id(name);
need_update = theme_icon_is_fallback(view->icon) && buffer_changed;
if (need_update || old != view->icon) {
wl_signal_emit_mutable(&view->events.icon_update, NULL);
}
}
static void view_end_keyboard_grab(struct view *view)
{
struct view *descendant = view_find_descendant_modal(view);
if (descendant) {
view = descendant;
}
if (!KYWC_VIEW_IS_FOCUSABLE(&view->base)) {
return;
}
struct seat *seat = view->base.focused_seat;
if (!seat_is_dragging(seat)) {
wlr_seat_keyboard_end_grab(seat->wlr_seat);
}
}
static void action_effect_options_adjust(enum action_effect_options_step step,
enum effect_action action,
struct action_effect_options *options, void *user_data)
{
switch (step) {
case ACTION_EFFECT_OPTIONS_ADD:
if (options->effect_type == ACTION_EFFECT_FADE) {
struct view *view = user_data;
options->surface = view->surface;
if (action == EFFECT_ACTION_MAP) {
options->width_scale = 0.9f;
options->height_scale = 0.9f;
options->duration = 300;
} else if (action == EFFECT_ACTION_UNMAP) {
options->width_scale = 0.8f;
options->height_scale = 0.8f;
options->duration = 260;
}
options->scale = view->surface ? ky_scene_surface_get_scale(options->surface) : 1.0f;
}
break;
case ACTION_EFFECT_OPTIONS_SURFACE:
case ACTION_EFFECT_OPTIONS_CONFIRM:
break;
}
}
void view_map(struct view *view)
{
struct kywc_view *kywc_view = &view->base;
wl_signal_emit_mutable(&kywc_view->events.premap, NULL);
view_set_icon(view, false);
if (view_manager->mode->impl->view_map) {
view_manager->mode->impl->view_map(view);
}
/* assume that request_minimize may emitted before map */
ky_scene_node_set_enabled(&view->tree->node, !kywc_view->minimized);
kywc_view->mapped = true;
if (view->pending.action) {
/* add a fallback geometry */
if (kywc_view->maximized || kywc_view->fullscreen) {
view_get_tiled_geometry(view, &view->saved.geometry, view->output, KYWC_TILE_CENTER);
}
if (view->impl->configure) {
view->impl->configure(view);
}
}
struct ky_scene_buffer *buffer = ky_scene_buffer_try_from_surface(view->surface);
if (buffer->primary_output) {
view_update_output(view, &output_from_wlr_output(buffer->primary_output->output)->base);
}
wl_signal_add(&buffer->events.outputs_update, &view->outputs_update);
kywc_view->has_initial_position = false;
kywc_view_activate(kywc_view);
view_end_keyboard_grab(view);
view_set_focus(view, kywc_view->focused_seat);
view_update_round_corner(view);
if (view->parent && view->parent->base.mapped) {
kywc_view->skip_taskbar = true;
kywc_view->skip_switcher = true;
}
kywc_log(KYWC_DEBUG, "Kywc_view %p map", kywc_view);
if (!view_add_slide_effect(view, true) && kywc_view->role == KYWC_VIEW_ROLE_NORMAL) {
if (view_manager->impl.get_startup_geometry) {
view_manager->impl.get_startup_geometry(view, view_manager);
kywc_log(KYWC_DEBUG, "%s startup_geometry is %d, %d, %d, %d", kywc_view->app_id,
view->startup_geometry.x, view->startup_geometry.y,
view->startup_geometry.width, view->startup_geometry.height);
}
if (kywc_box_not_empty(&view->startup_geometry)) {
view_add_scale_effect(view, SCALE_MINIMIZE);
} else {
view_add_action_effect(view, EFFECT_ACTION_MAP, ACTION_EFFECT_FADE,
action_effect_options_adjust, view);
}
}
/* clear startup geometry */
view->startup_geometry = (struct kywc_box){ 0 };
wl_signal_emit_mutable(&kywc_view->events.map, NULL);
wl_signal_emit_mutable(&view_manager->events.new_mapped_view, kywc_view);
struct view_proxy *proxy;
wl_list_for_each(proxy, &view->view_proxies, view_link) {
wl_signal_emit_mutable(&proxy->workspace->events.view_enter, view);
}
if (view->current_proxy && !kywc_view->minimized) {
if (view_manager->show_desktop_enabled) {
view_manager_show_desktop(false, false);
}
if (view_manager->show_activated_only_enabled) {
view_manager_show_activated_only(false, false);
}
}
cursor_rebase_all(false);
}
void view_unmap(struct view *view)
{
struct kywc_view *kywc_view = &view->base;
kywc_view->mapped = false;
kywc_log(KYWC_DEBUG, "Kywc_view %p unmap", kywc_view);
if (!kywc_view->minimized && !view_add_slide_effect(view, false) &&
kywc_view->role == KYWC_VIEW_ROLE_NORMAL) {
view_add_action_effect(view, EFFECT_ACTION_UNMAP, ACTION_EFFECT_FADE,
action_effect_options_adjust, view);
}
if (view == view_manager_get_global_authentication()) {
view_manager_set_global_authentication(NULL);
} else if (view == view_manager->desktop) {
view_manager->desktop = NULL;
}
wl_signal_emit_mutable(&kywc_view->events.unmap, NULL);
if (view_manager->mode->impl->view_unmap) {
view_manager->mode->impl->view_unmap(view);
}
struct view_proxy *proxy;
wl_list_for_each(proxy, &view->view_proxies, view_link) {
wl_signal_emit_mutable(&proxy->workspace->events.view_leave, view);
}
kywc_view->title = kywc_view->app_id = NULL;
wl_list_remove(&view->outputs_update.link);
wl_list_init(&view->outputs_update.link);
ky_scene_node_set_enabled(&view->tree->node, false);
if (view->pending.configure_timeout) {
wl_event_source_remove(view->pending.configure_timeout);
view->pending.configure_timeout = NULL;
}
wl_list_remove(&view->parent_link);
wl_list_init(&view->parent_link);
if (view->parent) {
view_update_capabilities(view->parent, KYWC_VIEW_MINIMIZABLE | KYWC_VIEW_MAXIMIZABLE |
KYWC_VIEW_FULLSCREENABLE | KYWC_VIEW_MOVABLE |
KYWC_VIEW_RESIZABLE);
view->parent = NULL;
}
struct view *child, *tmp;
wl_list_for_each_safe(child, tmp, &view->children, parent_link) {
wl_list_remove(&child->parent_link);
wl_list_init(&child->parent_link);
child->parent = NULL;
view_update_capabilities(child, KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_FULLSCREENABLE);
}
cursor_rebase_all(false);
}
static int view_handle_configure_timeout(void *data)
{
struct view *view = data;
kywc_log(KYWC_INFO, "Client (%s) did not respond to configure request", view->base.app_id);
if (view->impl->configure_timeout) {
view->impl->configure_timeout(view);
}
/* fallback for pending actions */
if (view_action_change_size(view->pending.configure_action)) {
struct kywc_box *pending = &view->pending.configure_geometry;
int x = pending->x, y = pending->y;
/* fix wobbling when user resize */
if (view->pending.configure_action & VIEW_ACTION_RESIZE) {
struct kywc_box *current = &view->base.geometry;
uint32_t resize_edges = view->current_resize_edges;
if (resize_edges & KYWC_EDGE_LEFT) {
x += pending->width - current->width;
}
if (resize_edges & KYWC_EDGE_TOP) {
y += pending->height - current->height;
}
}
view_helper_move(view, x, y);
}
view_configured(view, true);
return 0;
}
void view_configure(struct view *view, uint32_t serial)
{
view->pending.configure_serial = serial;
view->pending.configure_action |= view->pending.action;
view->pending.configure_geometry = view->pending.geometry;
view->pending.action = VIEW_ACTION_NOP;
view->pending.geometry = (struct kywc_box){ 0 };
view->pending.geometry = view->base.geometry;
if (serial == 0) {
return;
}
if (!view->pending.configure_timeout) {
view->pending.configure_timeout = wl_event_loop_add_timer(
view_manager->server->event_loop, view_handle_configure_timeout, view);
}
uint32_t timeout = view_manager_get_configure_timeout(view);
wl_event_source_timer_update(view->pending.configure_timeout, timeout);
}
void view_configured(struct view *view, bool reset)
{
struct kywc_view *kywc_view = &view->base;
if (view->pending.configure_timeout && reset) {
wl_event_source_remove(view->pending.configure_timeout);
view->pending.configure_timeout = NULL;
}
if (view->pending.configure_action == VIEW_ACTION_NOP) {
return;
}
if (view->pending.configure_action & VIEW_ACTION_FULLSCREEN) {
view_add_scale_effect(view, SCALE_FULLSCREEN);
wl_signal_emit_mutable(&kywc_view->events.fullscreen, NULL);
}
if (view->pending.configure_action & VIEW_ACTION_MAXIMIZE) {
view_add_scale_effect(view, SCALE_MAXIMIZE);
wl_signal_emit_mutable(&kywc_view->events.maximize, NULL);
}
if (view->pending.configure_action & VIEW_ACTION_TILE) {
wl_signal_emit_mutable(&kywc_view->events.tile, NULL);
}
if ((view->pending.configure_action & VIEW_ACTION_RESIZE) == 0 && !view->interactive_moving) {
cursor_rebase_all(true);
}
if (view->pending.configure_action &
(VIEW_ACTION_FULLSCREEN | VIEW_ACTION_TILE | VIEW_ACTION_MAXIMIZE)) {
view_update_round_corner(view);
}
view->pending.configure_serial = 0;
view->pending.configure_action = VIEW_ACTION_NOP;
view->pending.configure_geometry = (struct kywc_box){ 0 };
view->pending.geometry = (struct kywc_box){ 0 };
}
void view_send_ping(struct view *view)
{
if (view->impl->ping) {
view->impl->ping(view);
}
}
void view_proxy_destroy(struct view_proxy *view_proxy)
{
if (!view_proxy) {
return;
}
if (view_proxy->view->base.mapped) {
wl_signal_emit_mutable(&view_proxy->workspace->events.view_leave, view_proxy->view);
wl_signal_emit_mutable(&view_proxy->view->events.workspace_leave, view_proxy->workspace);
}
wl_list_remove(&view_proxy->view_link);
wl_list_remove(&view_proxy->workspace_link);
ky_scene_node_destroy(&view_proxy->tree->node);
free(view_proxy);
}
void view_set_current_proxy(struct view *view, struct view_proxy *view_proxy)
{
if (view->current_proxy == view_proxy) {
return;
}
if (!view_proxy) {
assert(wl_list_empty(&view->view_proxies));
}
if (view_proxy) {
ky_scene_node_reparent(&view->tree->node, view_proxy->tree);
}
view->current_proxy = view_proxy;
view_update_capabilities(view, KYWC_VIEW_ABOVEABLE | KYWC_VIEW_BELOWABLE);
wl_signal_emit_mutable(&view->events.workspace, NULL);
}
static void view_proxies_destroy(struct view *view)
{
struct view_proxy *proxy, *tmp;
wl_list_for_each_safe(proxy, tmp, &view->view_proxies, view_link) {
view_proxy_destroy(proxy);
}
view_set_current_proxy(view, NULL);
}
void view_destroy(struct view *view)
{
struct kywc_view *kywc_view = &view->base;
kywc_log(KYWC_DEBUG, "Kywc_view %p destroy", kywc_view);
wl_signal_emit_mutable(&kywc_view->events.destroy, NULL);
wl_list_remove(&view->link);
wl_list_remove(&view->output_destroy.link);
wl_list_remove(&view->outputs_update.link);
wl_list_remove(&view->update_capabilities.link);
assert(wl_list_empty(&view->events.output.listener_list));
assert(wl_list_empty(&view->events.parent.listener_list));
assert(wl_list_empty(&view->events.workspace.listener_list));
assert(wl_list_empty(&view->events.workspace_enter.listener_list));
assert(wl_list_empty(&view->events.workspace_leave.listener_list));
assert(wl_list_empty(&view->events.icon_update.listener_list));
assert(wl_list_empty(&view->events.position.listener_list));
assert(wl_list_empty(&view->events.update_capabilities.listener_list));
assert(wl_list_empty(&kywc_view->events.premap.listener_list));
assert(wl_list_empty(&kywc_view->events.map.listener_list));
assert(wl_list_empty(&kywc_view->events.unmap.listener_list));
assert(wl_list_empty(&kywc_view->events.destroy.listener_list));
assert(wl_list_empty(&kywc_view->events.activate.listener_list));
assert(wl_list_empty(&kywc_view->events.maximize.listener_list));
assert(wl_list_empty(&kywc_view->events.minimize.listener_list));
assert(wl_list_empty(&kywc_view->events.fullscreen.listener_list));
assert(wl_list_empty(&kywc_view->events.tile.listener_list));
assert(wl_list_empty(&kywc_view->events.above.listener_list));
assert(wl_list_empty(&kywc_view->events.below.listener_list));
assert(wl_list_empty(&kywc_view->events.sticky.listener_list));
assert(wl_list_empty(&kywc_view->events.skip_taskbar.listener_list));
assert(wl_list_empty(&kywc_view->events.skip_switcher.listener_list));
assert(wl_list_empty(&kywc_view->events.demands_attention.listener_list));
assert(wl_list_empty(&kywc_view->events.modal.listener_list));
assert(wl_list_empty(&kywc_view->events.capabilities.listener_list));
assert(wl_list_empty(&kywc_view->events.title.listener_list));
assert(wl_list_empty(&kywc_view->events.app_id.listener_list));
assert(wl_list_empty(&kywc_view->events.position.listener_list));
assert(wl_list_empty(&kywc_view->events.size.listener_list));
assert(wl_list_empty(&kywc_view->events.decoration.listener_list));
// some apps might be destroyed directly without a map
struct view *child, *tmp;
wl_list_for_each_safe(child, tmp, &view->children, parent_link) {
wl_list_remove(&child->parent_link);
wl_list_init(&child->parent_link);
child->parent = NULL;
view_update_capabilities(child, KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_FULLSCREENABLE);
}
ky_scene_node_destroy(&view->tree->node);
view_proxies_destroy(view);
free(view->icon_name);
free((void *)kywc_view->uuid);
view->impl->destroy(view);
}
void view_set_title(struct view *view, const char *title)
{
struct kywc_view *kywc_view = &view->base;
kywc_view->title = title;
kywc_log(KYWC_DEBUG, "Kywc_view %p title %s", kywc_view, title);
wl_signal_emit_mutable(&kywc_view->events.title, NULL);
}
void view_set_app_id(struct view *view, const char *app_id)
{
struct kywc_view *kywc_view = &view->base;
kywc_view->app_id = app_id;
kywc_log(KYWC_DEBUG, "Kywc_view %p app_id %s", kywc_view, app_id);
if (kywc_view->mapped && !view->icon_name) {
view_set_icon(view, false);
}
wl_signal_emit_mutable(&kywc_view->events.app_id, NULL);
}
void view_set_decoration(struct view *view, enum kywc_ssd ssd)
{
struct kywc_view *kywc_view = &view->base;
if (kywc_view->ssd == ssd) {
return;
}
kywc_view->ssd = ssd;
view_update_round_corner(view);
kywc_log(KYWC_DEBUG, "Kywc_view %p need ssd %d", kywc_view, ssd);
wl_signal_emit_mutable(&kywc_view->events.decoration, NULL);
}
void view_set_icon_name(struct view *view, const char *icon_name)
{
if ((!view->icon_name && !icon_name) ||
(view->icon_name && icon_name && !strcmp(view->icon_name, icon_name))) {
return;
}
free(view->icon_name);
view->icon_name = icon_name ? strdup(icon_name) : NULL;
if (view->base.mapped) {
view_set_icon(view, false);
}
}
struct view_proxy *view_proxy_by_workspace(struct view *view, struct workspace *workspace)
{
struct view_proxy *view_proxy;
wl_list_for_each(view_proxy, &view->view_proxies, view_link) {
if (view_proxy->workspace == workspace) {
return view_proxy;
}
}
return NULL;
}
static struct view_proxy *view_proxy_create(struct view *view, struct workspace *workspace)
{
struct view_proxy *proxy = calloc(1, sizeof(*proxy));
if (!proxy) {
return NULL;
}
proxy->view = view;
proxy->workspace = workspace;
wl_list_insert(&workspace->view_proxies, &proxy->workspace_link);
wl_list_insert(&view->view_proxies, &proxy->view_link);
/* create view_proxy tree from new workspace view_layer */
enum layer layer =
view->base.kept_above ? LAYER_ABOVE : (view->base.kept_below ? LAYER_BELOW : LAYER_NORMAL);
struct view_layer *view_layer = workspace_layer(workspace, layer);
proxy->tree = ky_scene_tree_create(view_layer->tree);
if (view->base.mapped) {
wl_signal_emit_mutable(&workspace->events.view_enter, view);
wl_signal_emit_mutable(&view->events.workspace_enter, workspace);
}
return proxy;
}
struct view *view_find_ancestor(struct view *view)
{
struct view *ancestor = view;
while (ancestor->parent) {
ancestor = ancestor->parent;
}
return ancestor;
}
static void view_do_set_workspace(struct view *view, struct workspace *workspace)
{
struct view_proxy *proxy = view_proxy_by_workspace(view, workspace);
if (!proxy) {
proxy = view_proxy_create(view, workspace);
if (!proxy) {
return;
}
}
if (proxy->tree != view->tree->node.parent) {
view_set_current_proxy(view, proxy);
}
// destroy all proxies except the new worskpace
struct view_proxy *view_proxy, *tmp;
wl_list_for_each_safe(view_proxy, tmp, &view->view_proxies, view_link) {
if (view_proxy != proxy) {
view_proxy_destroy(view_proxy);
}
}
if (view->base.sticky) {
view->base.sticky = false;
wl_signal_emit_mutable(&view->base.events.sticky, NULL);
}
struct view *child;
wl_list_for_each_reverse(child, &view->children, parent_link) {
view_do_set_workspace(child, workspace);
}
}
void view_set_workspace(struct view *view, struct workspace *workspace)
{
assert(view && workspace);
struct view *ancestor = view_find_ancestor(view);
view_do_set_workspace(ancestor, workspace);
}
static void view_do_unset_workspace(struct view *view, struct view_layer *layer)
{
/* set workspace to null must reparent view tree first */
view_proxies_destroy(view);
wl_list_init(&view->view_proxies);
struct view *child;
wl_list_for_each_reverse(child, &view->children, parent_link) {
ky_scene_node_reparent(&child->tree->node, layer->tree);
view_do_unset_workspace(child, layer);
}
}
void view_unset_workspace(struct view *view, struct view_layer *layer)
{
assert(layer->layer != LAYER_BELOW && layer->layer != LAYER_NORMAL &&
layer->layer != LAYER_ABOVE && view);
struct view *ancestor = view_find_ancestor(view);
ky_scene_node_reparent(&ancestor->tree->node, layer->tree);
view_do_unset_workspace(ancestor, layer);
}
static struct view_proxy *view_do_add_workspace(struct view *view, struct workspace *workspace)
{
struct view_proxy *view_proxy = view_proxy_by_workspace(view, workspace);
if (!view_proxy) {
view_proxy = view_proxy_create(view, workspace);
struct workspace *current_workspace = workspace_manager_get_current();
if (workspace == current_workspace) {
view_set_current_proxy(view, view_proxy);
}
}
struct view *child;
wl_list_for_each_reverse(child, &view->children, parent_link) {
view_do_add_workspace(child, workspace);
}
return view_proxy;
}
struct view_proxy *view_add_workspace(struct view *view, struct workspace *workspace)
{
if (!view || !workspace) {
return NULL;
}
struct view *ancestor = view_find_ancestor(view);
view_do_add_workspace(ancestor, workspace);
return view_proxy_by_workspace(view, workspace);
}
static void view_do_remove_workspace(struct view *view, struct workspace *workspace)
{
struct view_proxy *view_proxy = view_proxy_by_workspace(view, workspace);
if (!view_proxy) {
return;
}
struct view_proxy *proxy;
wl_list_for_each_reverse(proxy, &view->view_proxies, view_link) {
if (proxy != view_proxy) {
break;
}
}
/* add to all workspace if the view only exists in this workspace */
if (&proxy->view_link == &view->view_proxies) {
view_add_all_workspace(view);
return;
}
/* del the proxy if view exists in multi workspaces */
if (view->current_proxy == view_proxy) {
view_set_current_proxy(view, proxy);
}
view_proxy_destroy(view_proxy);
if (view->base.sticky) {
view->base.sticky = false;
wl_signal_emit_mutable(&view->base.events.sticky, NULL);
}
struct view *child;
wl_list_for_each_reverse(child, &view->children, parent_link) {
view_do_remove_workspace(child, workspace);
}
}
void view_remove_workspace(struct view *view, struct workspace *workspace)
{
if (!view || !workspace) {
return;
}
struct view *ancestor = view_find_ancestor(view);
view_do_remove_workspace(ancestor, workspace);
}
void view_add_all_workspace(struct view *view)
{
if (!view || view->base.sticky) {
return;
}
int workspace_num = workspace_manager_get_count();
struct workspace *workspace = NULL;
for (int i = 0; i < workspace_num; i++) {
workspace = workspace_by_position(i);
view_add_workspace(view, workspace);
}
view->base.sticky = true;
wl_signal_emit_mutable(&view->base.events.sticky, NULL);
}
static void view_set_layer_in_workspace(struct view *view, enum layer layer)
{
if (!view || !view->current_proxy) {
return;
}
struct view_layer *view_layer = NULL;
if (layer == LAYER_ACTIVE) {
view_layer = view_manager_get_layer(LAYER_ACTIVE, false);
ky_scene_node_reparent(&view->tree->node, view_layer->tree);
return;
}
struct view_proxy *proxy;
wl_list_for_each(proxy, &view->view_proxies, view_link) {
view_layer = workspace_layer(proxy->workspace, layer);
if (!view_layer) {
continue;
}
ky_scene_node_reparent(&proxy->tree->node, view_layer->tree);
}
}
static void view_follow_parent_layer(struct view *view, struct view *parent)
{
struct view_layer *layer = view_manager_get_layer_by_node(&parent->tree->node, false);
if (!layer) {
return;
}
if (view->current_proxy && parent->current_proxy) {
view_set_layer_in_workspace(view, layer->layer);
} else if (view->current_proxy && !parent->current_proxy) {
view_unset_workspace(view, layer);
} else if (!view->current_proxy && parent->current_proxy) {
view_add_workspace(view, parent->current_proxy->workspace);
view_set_layer_in_workspace(view, layer->layer);
} else {
ky_scene_node_reparent(&view->tree->node, layer->tree);
}
}
void view_add_parent_workspace(struct view *view)
{
if (!view || !view->parent) {
return;
}
struct view *parent = view->parent;
struct view_proxy *proxy;
wl_list_for_each(proxy, &parent->view_proxies, view_link) {
view_add_workspace(view, proxy->workspace);
}
}
void view_set_parent(struct view *view, struct view *parent)
{
if (view->parent == parent) {
return;
}
wl_list_remove(&view->parent_link);
if (parent) {
wl_list_insert(&parent->children, &view->parent_link);
struct view_layer *parent_layer =
view_manager_get_layer_by_node(&parent->tree->node, false);
struct view_layer *layer = view_manager_get_layer_by_node(&view->tree->node, false);
/* do not set layer if parent layer below view layer */
if (parent_layer && layer && parent_layer->layer > layer->layer) {
view_follow_parent_layer(view, parent);
}
} else {
wl_list_init(&view->parent_link);
}
kywc_log(KYWC_DEBUG, "View %p set parent to %p", view, parent);
view->parent = parent;
view_update_descendant_capabilities(parent ? parent : view,
KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_FULLSCREENABLE |
KYWC_VIEW_MOVABLE | KYWC_VIEW_RESIZABLE);
view_add_parent_workspace(view);
wl_signal_emit_mutable(&view->events.parent, NULL);
}
void kywc_view_add_new_listener(struct wl_listener *listener)
{
wl_signal_add(&view_manager->events.new_view, listener);
}
void kywc_view_add_new_mapped_listener(struct wl_listener *listener)
{
wl_signal_add(&view_manager->events.new_mapped_view, listener);
}
struct kywc_view *kywc_view_by_uuid(const char *uuid)
{
struct view *view;
wl_list_for_each(view, &view_manager->views, link) {
if (strcmp(uuid, view->base.uuid) == 0) {
return &view->base;
}
}
return NULL;
}
struct view *view_from_kywc_view(struct kywc_view *kywc_view)
{
struct view *view = wl_container_of(kywc_view, view, base);
return view;
}
struct view *view_try_from_wlr_surface(struct wlr_surface *wlr_surface)
{
return wlr_surface->data;
}
void kywc_view_close(struct kywc_view *kywc_view)
{
struct view *view = view_from_kywc_view(kywc_view);
if (!KYWC_VIEW_IS_CLOSEABLE(kywc_view)) {
return;
}
if (view->impl->close) {
view->impl->close(view);
}
view_send_ping(view);
}
void view_do_move(struct view *view, int x, int y)
{
view->pending.action |= VIEW_ACTION_MOVE;
view->pending.geometry.x = x;
view->pending.geometry.y = y;
if (view->base.mapped && view->impl->configure) {
view->impl->configure(view);
}
}
void kywc_view_move(struct kywc_view *kywc_view, int x, int y)
{
if (view_manager->mode->impl->view_request_move) {
view_manager->mode->impl->view_request_move(view_from_kywc_view(kywc_view), x, y);
}
}
void view_do_resize(struct view *view, struct kywc_box *geometry)
{
view->pending.action |= VIEW_ACTION_RESIZE;
view->pending.geometry = *geometry;
if (view->base.mapped && view->impl->configure) {
view->impl->configure(view);
}
}
void kywc_view_resize(struct kywc_view *kywc_view, struct kywc_box *geometry)
{
if (view_manager->mode->impl->view_request_resize) {
view_manager->mode->impl->view_request_resize(view_from_kywc_view(kywc_view), geometry);
}
}
static void view_set_activated(struct view *view, bool activated);
static void handle_activated_view_minimized(struct wl_listener *listener, void *data)
{
struct view *view = view_manager->activated.view;
if (!view->base.minimized) {
return;
}
view_set_activated(view, false);
view_activate_topmost(false);
}
static void handle_activated_view_unmap(struct wl_listener *listener, void *data)
{
struct view *view = view_manager->activated.view;
view_set_activated(view, false);
view_activate_topmost(false);
}
static void view_reparent_fullscreen(struct view *view, bool active);
static void view_set_activated(struct view *view, bool activated)
{
struct kywc_view *kywc_view = &view->base;
if (kywc_view->activated == activated) {
return;
}
if (activated) {
/* listen activated view's minimize and unmap signals,
* so that we can auto activate another view.
*/
view_manager->activated.view = view;
wl_signal_add(&kywc_view->events.minimize, &view_manager->activated.minimize);
wl_signal_add(&kywc_view->events.unmap, &view_manager->activated.unmap);
} else {
wl_list_remove(&view_manager->activated.minimize.link);
wl_list_remove(&view_manager->activated.unmap.link);
view_manager->activated.view = NULL;
}
/* change fullscreen layer when activation changed */
if (kywc_view->fullscreen) {
view_reparent_fullscreen(view, activated);
}
/* change parent fullscreen layer if parent is fullscreen */
struct view *ancestor = view_find_fullscreen_ancestor(view);
if (ancestor) {
view_reparent_fullscreen(ancestor, activated);
}
if (kywc_view->minimized && activated) {
kywc_view_set_minimized(kywc_view, false);
}
kywc_view->activated = activated;
view->pending.action |= VIEW_ACTION_ACTIVATE;
if (kywc_view->mapped && view->impl->configure) {
view->impl->configure(view);
}
wl_signal_emit_mutable(&kywc_view->events.activate, NULL);
}
static struct view *view_find_fullscreen_ancestor(struct view *view)
{
if (!view || !view->parent) {
return NULL;
}
struct view *ancestor = NULL;
while (view->parent) {
if (view->parent->base.fullscreen) {
ancestor = view->parent;
}
view = view->parent;
}
return ancestor;
}
void view_do_activate(struct view *view)
{
if (view && !KYWC_VIEW_IS_ACTIVATABLE(&view->base)) {
return;
}
struct view *last = view_manager->activated.view;
if (last != view) {
if (view_manager->show_activated_only_enabled) {
view_manager_show_activated_only(false, false);
}
if (last) {
view_set_activated(last, false);
}
if (view) {
view_set_activated(view, true);
}
wl_signal_emit_mutable(&view_manager->events.activate_view, view_manager->activated.view);
} else if (view) {
struct view *ancestor = view_find_fullscreen_ancestor(view);
if (!ancestor) {
ancestor = view;
}
if (ancestor->base.fullscreen) {
view_reparent_fullscreen(ancestor, true);
}
}
if (!view) {
return;
}
struct workspace *workspace = view->current_proxy ? view->current_proxy->workspace : NULL;
if (workspace && workspace != workspace_manager_get_current()) {
workspace_activate_with_effect(view->current_proxy->workspace);
}
}
static void view_hide_fullscreen_view_in_empty_workspace(void)
{
struct workspace *workspace = workspace_manager_get_current();
struct view *view = view_manager->activated.view;
if (!view) {
return;
}
struct view *ancestor = view_find_fullscreen_ancestor(view);
if (!ancestor) {
ancestor = view;
}
if (ancestor->base.fullscreen) {
struct view_proxy *view_proxy = view_proxy_by_workspace(ancestor, workspace);
if (!view_proxy) {
view_reparent_fullscreen(ancestor, false);
view_set_activated(ancestor, false);
}
}
}
void view_activate_topmost(bool force)
{
struct view *view = view_manager_get_global_authentication();
/* activate desktop at last */
struct view *current_view = NULL;
if (view_manager->activated.view &&
view_manager->activated.view->base.role != KYWC_VIEW_ROLE_DESKTOP) {
current_view = view_manager->activated.view;
}
if (!view && current_view && (!force || !current_view->current_proxy) &&
current_view->base.mapped && !current_view->base.minimized) {
/* keep current activated view */
view = current_view;
}
if (!view) {
struct view_proxy *view_proxy;
struct workspace *workspace = workspace_manager_get_current();
/* find topmost enabled(mapped and not minimized) view and activate it */
wl_list_for_each(view_proxy, &workspace->view_proxies, workspace_link) {
if (view_proxy->view->base.mapped && !view_proxy->view->base.minimized &&
KYWC_VIEW_IS_ACTIVATABLE(&view_proxy->view->base)) {
view = view_proxy->view;
break;
}
}
}
if (!view) {
view = view_manager->desktop;
}
if (view) {
view_do_activate(view);
view_set_focus(view, input_manager_get_default_seat());
return;
}
view_do_activate(NULL);
/* clear keyboard focus */
seat_focus_surface(input_manager_get_default_seat(), NULL);
/* workaround to hide fullscreen view when no view in workspace */
view_hide_fullscreen_view_in_empty_workspace();
wl_signal_emit_mutable(&view_manager->events.activate_view, view_manager->activated.view);
}
void view_raise_to_top(struct view *view, bool find_parent)
{
if (!view) {
return;
}
if (view->parent && find_parent) {
view_raise_to_top(view->parent, true);
}
if (view->current_proxy) {
/* insert view proxy in workspace topmost */
struct view_proxy *view_proxy;
wl_list_for_each(view_proxy, &view->view_proxies, view_link) {
wl_list_remove(&view_proxy->workspace_link);
wl_list_insert(&view_proxy->workspace->view_proxies, &view_proxy->workspace_link);
ky_scene_node_raise_to_top(&view_proxy->tree->node);
}
} else {
ky_scene_node_raise_to_top(&view->tree->node);
}
/* raise children if any */
struct view *child;
wl_list_for_each(child, &view->children, parent_link) {
view_raise_to_top(child, false);
}
}
void kywc_view_activate(struct kywc_view *kywc_view)
{
struct view *view = kywc_view ? view_from_kywc_view(kywc_view) : NULL;
if (!view) {
view_do_activate(NULL);
return;
}
if (view_manager->mode->impl->view_request_activate) {
view_manager->mode->impl->view_request_activate(view);
}
}
void view_set_focus(struct view *view, struct seat *seat)
{
if (!view || !seat) {
return;
}
struct view *descendant = view_find_descendant_modal(view);
if (descendant) {
view = descendant;
}
if (!KYWC_VIEW_IS_FOCUSABLE(&view->base)) {
return;
}
seat_focus_surface(seat, view->surface);
view_send_ping(view);
}
void view_do_tiled(struct view *view, enum kywc_tile tile, struct kywc_output *kywc_output,
struct kywc_box *geometry)
{
struct kywc_view *kywc_view = &view->base;
/* tiled mode may switch between outputs */
if (kywc_view->tiled == tile && (!kywc_output || kywc_output == view->output) &&
kywc_box_equal(&kywc_view->geometry, geometry)) {
if (kywc_box_not_empty(&view->tile_start_geometry)) {
view_add_scale_effect(view, SCALE_RESIZE);
}
return;
}
/* may switch between tiled modes */
if (kywc_view->tiled == KYWC_TILE_NONE && tile != KYWC_TILE_NONE) {
if (!kywc_view->maximized && !view->interactive_moving &&
(!kywc_output || kywc_output == view->output)) {
view->saved.geometry = view_action_change_size(view->pending.configure_action)
? view->pending.configure_geometry
: kywc_view->geometry;
}
}
if (kywc_view->maximized) {
view->pending.action |= VIEW_ACTION_MAXIMIZE;
kywc_view->maximized = false;
}
kywc_view->tiled = tile;
view->pending.action |= VIEW_ACTION_TILE;
view->pending.geometry = *geometry;
view_add_scale_effect(view, SCALE_RESIZE);
if (kywc_view->mapped && view->impl->configure) {
view->impl->configure(view);
}
}
void kywc_view_set_tiled(struct kywc_view *kywc_view, enum kywc_tile tile,
struct kywc_output *kywc_output)
{
if (view_manager->mode->impl->view_request_tiled) {
view_manager->mode->impl->view_request_tiled(view_from_kywc_view(kywc_view), tile,
kywc_output);
}
}
void view_do_minimized(struct view *view, bool minimized)
{
struct kywc_view *kywc_view = &view->base;
if (kywc_view->minimized == minimized) {
return;
}
kywc_view->minimized = minimized;
view->pending.action |= VIEW_ACTION_MINIMIZE;
if (!kywc_view->mapped) {
return;
}
ky_scene_node_set_enabled(&view->tree->node, !minimized);
if (view->impl->configure) {
view->impl->configure(view);
}
bool used_slide = false;
if (kywc_view->role == KYWC_VIEW_ROLE_SYSTEMWINDOW) {
used_slide = view_add_slide_effect(view, !kywc_view->minimized);
}
if (!used_slide) {
/* if view is the activated view, process it in activated.minimize listener */
if (view_manager->state.minimize_effect_type == MINIMIZE_EFFECT_TYPE_MAGIC_LAMP) {
if (!view_add_magic_lamp_effect(view)) {
view_add_scale_effect(view, SCALE_MINIMIZE);
}
} else {
view_add_scale_effect(view, SCALE_MINIMIZE);
}
}
wl_signal_emit_mutable(&kywc_view->events.minimize, NULL);
if (!kywc_view->minimized) {
if (view_manager->show_desktop_enabled) {
view_manager_show_desktop(false, false);
}
if (view_manager->show_activated_only_enabled) {
view_manager_show_activated_only(false, false);
}
}
cursor_rebase_all(false);
if (!view->minimized_when_show_desktop) {
struct view *child;
wl_list_for_each(child, &view->children, parent_link) {
view_do_minimized(child, minimized);
}
}
}
void kywc_view_set_minimized(struct kywc_view *kywc_view, bool minimized)
{
if (view_manager->mode->impl->view_request_minimized) {
view_manager->mode->impl->view_request_minimized(view_from_kywc_view(kywc_view), minimized);
}
}
void kywc_view_toggle_minimized(struct kywc_view *kywc_view)
{
kywc_view_set_minimized(kywc_view, !kywc_view->minimized);
}
void view_do_maximized(struct view *view, bool maximized, struct kywc_output *kywc_output)
{
struct kywc_view *kywc_view = &view->base;
/* tiled to unmaximized after tiled from maximized */
if (!kywc_view->tiled && kywc_view->maximized == maximized &&
(!kywc_output || kywc_output == view->output)) {
return;
}
struct kywc_box geo = { 0 };
if (maximized) {
view_get_tiled_geometry(view, &geo, kywc_output ? kywc_output : view->output,
KYWC_TILE_ALL);
if (!kywc_view->tiled && !view->interactive_moving &&
(!kywc_output || kywc_output == view->output)) {
view->saved.geometry = view_action_change_size(view->pending.configure_action)
? view->pending.configure_geometry
: kywc_view->geometry;
}
} else {
if (kywc_box_not_empty(&view->restore_geometry)) {
geo = view->restore_geometry;
} else {
geo = view->saved.geometry;
}
}
/* don't restore tiled mode followed other compositors */
if (kywc_view->tiled) {
view->pending.action |= VIEW_ACTION_TILE;
kywc_view->tiled = KYWC_TILE_NONE;
}
kywc_view->maximized = maximized;
view->pending.action |= VIEW_ACTION_MAXIMIZE;
view->pending.geometry = geo;
if (kywc_view->mapped && view->impl->configure) {
view->impl->configure(view);
}
}
void kywc_view_set_maximized(struct kywc_view *kywc_view, bool maximized,
struct kywc_output *kywc_output)
{
if (view_manager->mode->impl->view_request_maximized) {
view_manager->mode->impl->view_request_maximized(view_from_kywc_view(kywc_view), maximized,
kywc_output);
}
}
void kywc_view_toggle_maximized(struct kywc_view *kywc_view)
{
kywc_view_set_maximized(kywc_view, !kywc_view->maximized, NULL);
}
static void view_reparent_fullscreen(struct view *view, bool active)
{
struct view_layer *layer = NULL;
if (active) {
layer = view_manager_get_layer(LAYER_ACTIVE, false);
if (layer->tree == view->tree->node.parent) {
ky_scene_node_raise_to_top(&view->tree->node);
} else {
ky_scene_node_reparent(&view->tree->node, layer->tree);
}
} else if (view->current_proxy) {
ky_scene_node_reparent(&view->tree->node, view->current_proxy->tree);
}
struct view *child;
wl_list_for_each(child, &view->children, parent_link) {
view_reparent_fullscreen(child, active);
}
}
void view_do_fullscreen(struct view *view, bool fullscreen, struct kywc_output *kywc_output)
{
struct kywc_view *kywc_view = &view->base;
if (kywc_view->fullscreen == fullscreen && (!kywc_output || kywc_output == view->output)) {
return;
}
struct kywc_box geo = { 0 };
if (fullscreen) {
kywc_output_effective_geometry(kywc_output ? kywc_output : view->output, &geo);
if (!kywc_view->maximized && !kywc_view->tiled) {
view->saved.geometry = view_action_change_size(view->pending.configure_action)
? view->pending.configure_geometry
: kywc_view->geometry;
}
} else if (kywc_view->maximized) {
view_get_tiled_geometry(view, &geo, view->output, KYWC_TILE_ALL);
} else if (kywc_view->tiled) {
view_get_tiled_geometry(view, &geo, view->output, kywc_view->tiled);
} else {
if (kywc_box_not_empty(&view->restore_geometry)) {
geo = view->restore_geometry;
} else {
geo = view->saved.geometry;
}
}
view_reparent_fullscreen(view, fullscreen);
kywc_view->fullscreen = fullscreen;
view_update_capabilities(view, KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_MOVABLE | KYWC_VIEW_RESIZABLE);
view->pending.action |= VIEW_ACTION_FULLSCREEN;
view->pending.geometry = geo;
if (kywc_view->mapped && view->impl->configure) {
view->impl->configure(view);
}
}
void kywc_view_set_fullscreen(struct kywc_view *kywc_view, bool fullscreen,
struct kywc_output *kywc_output)
{
if (view_manager->mode->impl->view_request_fullscreen) {
view_manager->mode->impl->view_request_fullscreen(view_from_kywc_view(kywc_view),
fullscreen, kywc_output);
}
}
void kywc_view_toggle_fullscreen(struct kywc_view *kywc_view)
{
kywc_view_set_fullscreen(kywc_view, !kywc_view->fullscreen, NULL);
}
static void view_do_set_kept_above(struct view *view, bool kept_above)
{
if (view->base.kept_above != kept_above) {
view->base.kept_above = kept_above;
view->base.kept_below = false;
enum layer layer = kept_above ? LAYER_ABOVE : LAYER_NORMAL;
struct view_proxy *proxy;
wl_list_for_each(proxy, &view->view_proxies, view_link) {
struct view_layer *view_layer = workspace_layer(proxy->workspace, layer);
ky_scene_node_reparent(&proxy->tree->node, view_layer->tree);
}
wl_signal_emit_mutable(&view->base.events.above, NULL);
}
if (kept_above) {
struct view *child;
wl_list_for_each_reverse(child, &view->children, parent_link) {
view_do_set_kept_above(child, kept_above);
}
} else if (view->parent) {
view_do_set_kept_above(view->parent, kept_above);
}
}
void kywc_view_set_kept_above(struct kywc_view *kywc_view, bool kept_above)
{
struct view *view = view_from_kywc_view(kywc_view);
if (!KYWC_VIEW_IS_ABOVEABLE(kywc_view)) {
return;
}
view_do_set_kept_above(view, kept_above);
view_raise_to_top(view, false);
}
void kywc_view_toggle_kept_above(struct kywc_view *kywc_view)
{
kywc_view_set_kept_above(kywc_view, !kywc_view->kept_above);
}
static void view_do_set_kept_below(struct view *view, bool kept_below)
{
/* ancestor may already keep below, skip and go on */
if (view->base.kept_below != kept_below) {
view->base.kept_below = kept_below;
view->base.kept_above = false;
enum layer layer = kept_below ? LAYER_BELOW : LAYER_NORMAL;
struct view_proxy *proxy;
wl_list_for_each(proxy, &view->view_proxies, view_link) {
struct view_layer *view_layer = workspace_layer(proxy->workspace, layer);
ky_scene_node_reparent(&proxy->tree->node, view_layer->tree);
}
wl_signal_emit_mutable(&view->base.events.below, NULL);
}
if (!kept_below) {
struct view *child;
wl_list_for_each_reverse(child, &view->children, parent_link) {
view_do_set_kept_below(child, kept_below);
}
} else if (view->parent) {
view_do_set_kept_below(view->parent, kept_below);
}
}
void kywc_view_set_kept_below(struct kywc_view *kywc_view, bool kept_below)
{
struct view *view = view_from_kywc_view(kywc_view);
if (!KYWC_VIEW_IS_BELOWABLE(kywc_view)) {
return;
}
view_do_set_kept_below(view, kept_below);
view_raise_to_top(view, false);
}
void kywc_view_toggle_kept_below(struct kywc_view *kywc_view)
{
kywc_view_set_kept_below(kywc_view, !kywc_view->kept_below);
}
void kywc_view_set_skip_taskbar(struct kywc_view *kywc_view, bool skip_taskbar)
{
if (kywc_view->skip_taskbar == skip_taskbar) {
return;
}
kywc_view->skip_taskbar = skip_taskbar;
wl_signal_emit_mutable(&kywc_view->events.skip_taskbar, NULL);
}
void kywc_view_set_skip_switcher(struct kywc_view *kywc_view, bool skip_switcher)
{
if (kywc_view->skip_switcher == skip_switcher) {
return;
}
kywc_view->skip_switcher = skip_switcher;
wl_signal_emit_mutable(&kywc_view->events.skip_switcher, NULL);
}
void kywc_view_set_demands_attention(struct kywc_view *kywc_view, bool demands_attention)
{
if (kywc_view->demands_attention == demands_attention) {
return;
}
kywc_view->demands_attention = demands_attention;
wl_signal_emit_mutable(&kywc_view->events.demands_attention, NULL);
}
void kywc_view_set_modal(struct kywc_view *kywc_view, bool modal)
{
if (kywc_view->modal == modal) {
return;
}
kywc_view->modal = modal;
struct view *ancestor = view_find_ancestor(view_from_kywc_view(kywc_view));
view_update_descendant_capabilities(
ancestor, KYWC_VIEW_MINIMIZABLE | KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_CLOSEABLE |
KYWC_VIEW_FULLSCREENABLE | KYWC_VIEW_MOVABLE | KYWC_VIEW_RESIZABLE |
KYWC_VIEW_MINIMIZE_BUTTON | KYWC_VIEW_MAXIMIZE_BUTTON);
wl_signal_emit_mutable(&kywc_view->events.modal, NULL);
}
void kywc_view_set_sticky(struct kywc_view *kywc_view, bool sticky)
{
if (kywc_view->sticky == sticky) {
return;
}
struct view *view = view_from_kywc_view(kywc_view);
if (sticky) {
view_add_all_workspace(view);
} else {
view_set_workspace(view, workspace_manager_get_current());
}
}
void view_helper_move(struct view *view, int x, int y)
{
struct kywc_box *geo = &view->base.geometry;
if (geo->x != x || geo->y != y) {
geo->x = x, geo->y = y;
ky_scene_node_set_position(&view->tree->node, x, y);
wl_signal_emit_mutable(&view->base.events.position, NULL);
}
if (!view->interactive_moving && view->current_resize_edges == KYWC_EDGE_NONE) {
wl_signal_emit_mutable(&view->events.position, NULL);
}
}
static bool view_has_modal_child(struct view *view)
{
struct view *child;
wl_list_for_each(child, &view->children, parent_link) {
if (child->base.mapped && (child->base.modal || view_has_modal_child(child))) {
return true;
}
}
return false;
}
bool view_has_modal_property(struct view *view)
{
return view->base.modal || view_has_modal_child(view);
}
struct view *view_find_descendant_modal(struct view *view)
{
struct view *child;
wl_list_for_each(child, &view->children, parent_link) {
if (!child->base.mapped || !child->base.modal) {
continue;
}
return view_find_descendant_modal(child);
}
return view->base.modal ? view : NULL;
}
static void view_handle_update_capabilities(struct wl_listener *listener, void *data)
{
struct view *view = wl_container_of(listener, view, update_capabilities);
struct kywc_view *kywc_view = &view->base;
struct view_update_capabilities_event *event = data;
if (event->mask & KYWC_VIEW_MINIMIZABLE) {
if (!view->minimized_when_show_desktop && view_has_modal_property(view)) {
event->state &= ~KYWC_VIEW_MINIMIZABLE;
}
}
if (event->mask & KYWC_VIEW_MAXIMIZABLE) {
if (kywc_view->fullscreen || view_has_modal_child(view) ||
KYWC_VIEW_HAS_FIXED_SIZE(kywc_view)) {
event->state &= ~KYWC_VIEW_MAXIMIZABLE;
}
}
if (event->mask & KYWC_VIEW_CLOSEABLE) {
if (view_has_modal_child(view)) {
event->state &= ~KYWC_VIEW_CLOSEABLE;
}
}
if (event->mask & KYWC_VIEW_FULLSCREENABLE) {
if (view_has_modal_child(view) || (KYWC_VIEW_HAS_FIXED_SIZE(kywc_view) && view->parent)) {
event->state &= ~KYWC_VIEW_FULLSCREENABLE;
}
}
if (event->mask & KYWC_VIEW_MOVABLE) {
if (kywc_view->fullscreen || view_has_modal_child(view)) {
event->state &= ~KYWC_VIEW_MOVABLE;
}
}
if (event->mask & KYWC_VIEW_RESIZABLE) {
if (kywc_view->fullscreen || view_has_modal_child(view) ||
KYWC_VIEW_HAS_FIXED_SIZE(kywc_view)) {
event->state &= ~KYWC_VIEW_RESIZABLE;
}
}
if (event->mask & KYWC_VIEW_ABOVEABLE) {
if (!view->current_proxy) {
event->state &= ~KYWC_VIEW_ABOVEABLE;
}
}
if (event->mask & KYWC_VIEW_BELOWABLE) {
if (!view->current_proxy) {
event->state &= ~KYWC_VIEW_BELOWABLE;
}
}
if (event->mask & KYWC_VIEW_MINIMIZE_BUTTON) {
if (kywc_view->modal) {
event->state &= ~KYWC_VIEW_MINIMIZE_BUTTON;
}
}
if (event->mask & KYWC_VIEW_MAXIMIZE_BUTTON) {
if (kywc_view->modal || KYWC_VIEW_HAS_FIXED_SIZE(kywc_view)) {
event->state &= ~KYWC_VIEW_MAXIMIZE_BUTTON;
}
}
}
struct view_layer *view_manager_get_layer_by_role(enum kywc_view_role role)
{
switch (role) {
default:
return NULL;
case KYWC_VIEW_ROLE_NORMAL:
return view_manager_get_layer(LAYER_NORMAL, true);
case KYWC_VIEW_ROLE_DESKTOP:
return view_manager_get_layer(LAYER_DESKTOP, false);
case KYWC_VIEW_ROLE_PANEL:
case KYWC_VIEW_ROLE_APPLETPOPUP:
return view_manager_get_layer(LAYER_DOCK, false);
case KYWC_VIEW_ROLE_ONSCREENDISPLAY:
return view_manager_get_layer(LAYER_ON_SCREEN_DISPLAY, false);
case KYWC_VIEW_ROLE_NOTIFICATION:
return view_manager_get_layer(LAYER_NOTIFICATION, false);
case KYWC_VIEW_ROLE_TOOLTIP:
return view_manager_get_layer(LAYER_POPUP, false);
case KYWC_VIEW_ROLE_CRITICALNOTIFICATION:
return view_manager_get_layer(LAYER_CRITICAL_NOTIFICATION, false);
case KYWC_VIEW_ROLE_SYSTEMWINDOW:
return view_manager_get_layer(LAYER_SYSTEM_WINDOW, false);
case KYWC_VIEW_ROLE_SWITCHER:
return view_manager_get_layer(LAYER_SWITCHER, false);
case KYWC_VIEW_ROLE_INPUTPANEL:
return view_manager_get_layer(LAYER_INPUT_PANEL, false);
case KYWC_VIEW_ROLE_LOGOUT:
return view_manager_get_layer(LAYER_LOGOUT, false);
case KYWC_VIEW_ROLE_SCREENLOCKNOTIFICATION:
return view_manager_get_layer(LAYER_SCREEN_LOCK_NOTIFICATION, false);
case KYWC_VIEW_ROLE_WATERMARK:
return view_manager_get_layer(LAYER_WATERMARK, false);
case KYWC_VIEW_ROLE_SCREENLOCK:
return view_manager_get_layer(LAYER_SCREEN_LOCK, false);
case KYWC_VIEW_ROLE_AUTHENTICATION:
return view_manager_get_layer(LAYER_CRITICAL_NOTIFICATION, false);
}
}
void view_set_role(struct view *view, enum kywc_view_role role)
{
struct kywc_view *kywc_view = &view->base;
if (kywc_view->role == role) {
return;
}
kywc_view->role = role;
if (kywc_view->role == KYWC_VIEW_ROLE_AUTHENTICATION) {
/* cleared when view is unmapoed */
global_authentication_create(view);
}
if (kywc_view->role == KYWC_VIEW_ROLE_DESKTOP) {
view_manager->desktop = view;
}
if (kywc_view->role == KYWC_VIEW_ROLE_NORMAL) {
view_set_workspace(view, workspace_manager_get_current());
} else {
struct view_layer *layer = view_manager_get_layer_by_role(kywc_view->role);
view_unset_workspace(view, layer);
}
kywc_view->capabilities = 0;
if (kywc_view->role == KYWC_VIEW_ROLE_NORMAL) {
kywc_view->capabilities |= KYWC_VIEW_CAPABILITIES_ALL;
} else { // not normal
if (kywc_view->role != KYWC_VIEW_ROLE_DESKTOP && kywc_view->role != KYWC_VIEW_ROLE_PANEL) {
kywc_view->capabilities |= KYWC_VIEW_CLOSEABLE;
}
if (kywc_view->role == KYWC_VIEW_ROLE_AUTHENTICATION) {
kywc_view->capabilities |= KYWC_VIEW_MOVABLE;
}
if (kywc_view->role == KYWC_VIEW_ROLE_PANEL) {
kywc_view->capabilities |= KYWC_VIEW_RESIZABLE;
}
if (kywc_view->role != KYWC_VIEW_ROLE_PANEL && kywc_view->role != KYWC_VIEW_ROLE_TOOLTIP &&
kywc_view->role != KYWC_VIEW_ROLE_WATERMARK &&
kywc_view->role != KYWC_VIEW_ROLE_SWITCHER &&
kywc_view->role != KYWC_VIEW_ROLE_NOTIFICATION) {
kywc_view->capabilities |= KYWC_VIEW_ACTIVATABLE;
}
if (kywc_view->role == KYWC_VIEW_ROLE_DESKTOP ||
kywc_view->role == KYWC_VIEW_ROLE_SYSTEMWINDOW ||
kywc_view->role == KYWC_VIEW_ROLE_SCREENLOCK ||
kywc_view->role == KYWC_VIEW_ROLE_APPLETPOPUP ||
kywc_view->role == KYWC_VIEW_ROLE_ONSCREENDISPLAY ||
kywc_view->role == KYWC_VIEW_ROLE_AUTHENTICATION) {
kywc_view->capabilities |= KYWC_VIEW_FOCUSABLE;
}
}
// panel need to be unconstrained
kywc_view->unconstrained = kywc_view->role == KYWC_VIEW_ROLE_PANEL;
kywc_view->has_round_corner = kywc_view->role == KYWC_VIEW_ROLE_NORMAL ||
kywc_view->role == KYWC_VIEW_ROLE_SYSTEMWINDOW ||
kywc_view->role == KYWC_VIEW_ROLE_APPLETPOPUP ||
kywc_view->role == KYWC_VIEW_ROLE_AUTHENTICATION;
view_update_round_corner(view);
}
void view_update_size(struct view *view, int width, int height, int min_width, int min_height,
int max_width, int max_height)
{
struct kywc_view *kywc_view = &view->base;
if (kywc_view->min_width != min_width || kywc_view->min_height != min_height) {
kywc_view->min_width = min_width;
kywc_view->min_height = min_height;
kywc_log(KYWC_DEBUG, "View %p minimal size to %d x %d", view, min_width, min_height);
view_update_capabilities(view, KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_FULLSCREENABLE |
KYWC_VIEW_RESIZABLE | KYWC_VIEW_MAXIMIZE_BUTTON);
}
if (kywc_view->max_width != max_width || kywc_view->max_height != max_height) {
kywc_view->max_width = max_width;
kywc_view->max_height = max_height;
kywc_log(KYWC_DEBUG, "View %p maximal size to %d x %d", view, max_width, max_height);
view_update_capabilities(view, KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_FULLSCREENABLE |
KYWC_VIEW_RESIZABLE | KYWC_VIEW_MAXIMIZE_BUTTON);
}
if (kywc_view->geometry.width != width || kywc_view->geometry.height != height) {
kywc_view->geometry.width = width;
kywc_view->geometry.height = height;
kywc_log(KYWC_DEBUG, "View %p size to %d x %d", view, width, height);
wl_signal_emit_mutable(&view->base.events.size, NULL);
}
}
void view_update_descendant_capabilities(struct view *view, uint32_t mask)
{
view_update_capabilities(view, mask);
struct view *child;
wl_list_for_each(child, &view->children, parent_link) {
view_update_descendant_capabilities(child, mask);
}
}
void view_update_capabilities(struct view *view, uint32_t mask)
{
struct kywc_view *kywc_view = &view->base;
struct view_update_capabilities_event event = {
.mask = mask,
.state = KYWC_VIEW_CAPABILITIES_ALL & mask,
};
wl_signal_emit_mutable(&view->events.update_capabilities, &event);
uint32_t cleared = kywc_view->capabilities & ~mask;
uint32_t new_state = event.state & mask;
uint32_t capabilities = cleared | new_state;
if (kywc_view->capabilities == capabilities) {
return;
}
mask = kywc_view->capabilities ^ capabilities;
kywc_view->capabilities = capabilities;
struct kywc_view_capabilities_event ev = {
.mask = mask,
};
wl_signal_emit_mutable(&kywc_view->events.capabilities, &ev);
}
void view_add_update_capabilities_listener(struct view *view, struct wl_listener *listener)
{
wl_signal_add(&view->events.update_capabilities, listener);
}
void view_close_popups(struct view *view)
{
if (view->impl->close_popups) {
view->impl->close_popups(view);
}
}
void view_show_window_menu(struct view *view, struct seat *seat, int x, int y)
{
if (view_manager->mode->impl->view_request_show_menu) {
view_manager->mode->impl->view_request_show_menu(view, seat, x, y);
}
}
bool view_show_tile_flyout(struct view *view, struct seat *seat, struct kywc_box *box)
{
if (view_manager->mode->impl->view_request_show_tile_flyout) {
view_manager->mode->impl->view_request_show_tile_flyout(view, seat, box);
return true;
}
return false;
}
bool view_show_tile_linkage_bar(struct view *view, uint32_t edges)
{
if (view_manager->mode->impl->view_request_show_tile_linkage_bar) {
view_manager->mode->impl->view_request_show_tile_linkage_bar(view, edges);
return true;
}
return false;
}
void highlight_view(struct view *view, bool enable)
{
view_manager->highlight = enable ? view : NULL;
struct workspace *workspace = workspace_manager_get_current();
struct view_proxy *view_proxy;
wl_list_for_each_reverse(view_proxy, &workspace->view_proxies, workspace_link) {
if (!view_proxy->view->base.mapped || view_proxy->view->base.minimized) {
continue;
}
ky_scene_node_set_enabled(&view_proxy->view->tree->node, !enable);
}
if (view) {
// Unset highlight window need to restore status when highlight window is minimized
ky_scene_node_set_enabled(&view->tree->node, enable ? enable : !view->base.minimized);
}
wl_signal_emit_mutable(&view_manager->events.highlight, view);
}
void view_manager_add_highlight_view_listener(struct wl_listener *listener)
{
wl_signal_add(&view_manager->events.highlight, listener);
}
struct view *view_manager_get_highlight(void)
{
return view_manager->highlight;
}
void view_show_hang_window(struct view *view)
{
// TODO: implement show_hang_window
kywc_log(KYWC_WARN, "%s view is not responding", view->base.app_id);
if (view_manager->impl.show_hang_window) {
view_manager->impl.show_hang_window(view);
}
}
void view_manager_show_desktop(bool enabled, bool apply)
{
if (view_manager->show_desktop_enabled == enabled) {
return;
}
view_manager->show_desktop_enabled = enabled;
/* minimize all view in current workspace */
struct workspace *workspace = workspace_manager_get_current();
struct view_proxy *view_proxy;
wl_list_for_each_reverse(view_proxy, &workspace->view_proxies, workspace_link) {
struct view *view = view_proxy->view;
/* skip views not mapped */
if (!view->base.mapped) {
continue;
}
/* skip restoring views not minimized by show desktop */
if (!enabled && !view->minimized_when_show_desktop) {
continue;
}
/* true only the view is not minimized when going show desktop */
if (enabled) {
view->minimized_when_show_desktop = !view->base.minimized;
view_update_capabilities(view, KYWC_VIEW_MINIMIZABLE);
}
/* don't restoring views if the state is breaked */
if (apply || view_has_modal_property(view)) {
kywc_view_set_minimized(&view->base, enabled);
}
if (!enabled) {
view->minimized_when_show_desktop = false;
view_update_capabilities(view, KYWC_VIEW_MINIMIZABLE);
}
}
if (apply && !enabled) {
view_activate_topmost(false);
}
/* set focus to desktop after showing desktop */
if (apply && enabled && view_manager->desktop) {
view_set_focus(view_manager->desktop, input_manager_get_default_seat());
}
wl_signal_emit_mutable(&view_manager->events.show_desktop, NULL);
}
void view_manager_add_show_desktop_listener(struct wl_listener *listener)
{
wl_signal_add(&view_manager->events.show_desktop, listener);
}
bool view_manager_get_show_desktop(void)
{
return view_manager->show_desktop_enabled;
}
bool view_manager_get_show_switcher(void)
{
return view_manager->switcher_shown;
}
void view_manager_show_activated_only(bool enabled, bool apply)
{
if (view_manager->show_activated_only_enabled == enabled) {
return;
}
view_manager->show_activated_only_enabled = enabled;
struct view *current_view = view_manager_get_activated();
if (!current_view) {
return;
}
/* minimize all view in current workspace */
struct workspace *workspace = workspace_manager_get_current();
struct view_proxy *view_proxy;
wl_list_for_each_reverse(view_proxy, &workspace->view_proxies, workspace_link) {
struct view *view = view_proxy->view;
if (!view->base.mapped || view == current_view || view_has_modal_property(view)) {
continue;
}
/* skip restoring views not minimized by show only current view */
if (!enabled && !view->minimized_when_show_active_only) {
continue;
}
if (enabled) {
view->minimized_when_show_active_only = !view->base.minimized;
}
/* don't restoring views if the state is breaked */
if (apply) {
if (view->base.minimized == enabled || !KYWC_VIEW_IS_MINIMIZABLE(&view->base)) {
continue;
}
ky_scene_node_set_enabled(&view->tree->node, !enabled);
view->base.minimized = enabled;
view->pending.action |= VIEW_ACTION_MINIMIZE;
if (view->impl->configure) {
view->impl->configure(view);
}
wl_signal_emit_mutable(&view->base.events.minimize, NULL);
}
if (!enabled) {
view->minimized_when_show_active_only = false;
}
}
}
bool view_manager_get_show_activte_only(void)
{
return view_manager->show_activated_only_enabled;
}
uint32_t view_manager_get_adsorption(void)
{
return view_manager->state.view_adsorption;
}
uint32_t view_manager_get_resize_filter(struct view *view)
{
if (xwayland_check_view(view)) {
return view_manager->state.resize_filter[1];
} else {
return view_manager->state.resize_filter[0];
}
}
uint32_t view_manager_get_configure_timeout(struct view *view)
{
if (!view) {
return MAX(view_manager->state.configure_timeout[0],
view_manager->state.configure_timeout[1]);
} else if (xwayland_check_view(view)) {
return view_manager->state.configure_timeout[1];
} else {
return view_manager->state.configure_timeout[0];
}
}
void view_manager_set_global_authentication(struct view *view)
{
view_manager->global_authentication_view = view;
}
struct view *view_manager_get_global_authentication(void)
{
return view_manager->global_authentication_view;
}
static void handle_server_ready(struct wl_listener *listener, void *data)
{
theme_manager_add_update_listener(&view_manager->theme_update, false);
theme_manager_add_icon_update_listener(&view_manager->theme_icon_update);
window_menu_manager_create(view_manager);
maximize_switcher_create(view_manager);
tile_flyout_manager_create(view_manager);
stack_mode_register(view_manager);
tablet_mode_register(view_manager);
if (!view_manager->mode) {
view_manager->mode = view_manager_mode_from_name("stack_mode");
}
if (view_manager->mode->impl->view_mode_enter) {
view_manager->mode->impl->view_mode_enter();
}
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
assert(wl_list_empty(&view_manager->events.new_view.listener_list));
assert(wl_list_empty(&view_manager->events.new_mapped_view.listener_list));
assert(wl_list_empty(&view_manager->events.show_desktop.listener_list));
assert(wl_list_empty(&view_manager->events.activate_view.listener_list));
assert(wl_list_empty(&view_manager->events.highlight.listener_list));
wl_list_remove(&view_manager->server_destroy.link);
wl_list_remove(&view_manager->server_ready.link);
wl_list_remove(&view_manager->server_terminate.link);
wl_list_remove(&view_manager->theme_update.link);
wl_list_remove(&view_manager->theme_icon_update.link);
struct view_mode *mode, *tmp;
wl_list_for_each_safe(mode, tmp, &view_manager->view_modes, link) {
if (mode->impl->mode_destroy) {
mode->impl->mode_destroy();
}
view_manager_mode_unregister(mode);
}
for (int layer = LAYER_FIRST; layer < LAYER_NUMBER; layer++) {
ky_scene_node_destroy(&view_manager->layers[layer].tree->node);
}
free(view_manager);
view_manager = NULL;
}
static void handle_server_terminate(struct wl_listener *listener, void *data)
{
view_manager->state.num_workspaces = workspace_manager_get_count();
view_write_config(view_manager);
}
static void handle_theme_update(struct wl_listener *listener, void *data)
{
struct theme_update_event *update_event = data;
if (!(update_event->update_mask & THEME_UPDATE_MASK_CORNER_RADIUS)) {
return;
}
struct view *view;
wl_list_for_each(view, &view_manager->views, link) {
view_update_round_corner(view);
}
}
static void handle_theme_icon_update(struct wl_listener *listener, void *data)
{
struct view *view;
wl_list_for_each(view, &view_manager->views, link) {
if (view->base.mapped) {
view_set_icon(view, false);
}
}
}
void view_manager_set_switcher_shown(bool shown)
{
view_manager->switcher_shown = shown;
}
void view_click(struct seat *seat, struct view *view, uint32_t button, bool pressed,
enum click_state state)
{
if (view_manager->mode->impl->view_click) {
view_manager->mode->impl->view_click(seat, view, button, pressed, state);
}
}
void view_hover(struct seat *seat, struct view *view)
{
if (view_manager->mode->impl->view_hover) {
view_manager->mode->impl->view_hover(seat, view);
}
}
struct view_mode *view_manager_mode_from_name(const char *name)
{
if (!name || !*name) {
return NULL;
}
struct view_mode *mode;
wl_list_for_each(mode, &view_manager->view_modes, link) {
if (mode->impl->name && strcmp(mode->impl->name, name) == 0) {
return mode;
}
}
return NULL;
}
bool view_manager_set_view_mode(const char *name)
{
struct view_mode *view_mode = view_manager_mode_from_name(name);
if (!view_mode) {
return false;
}
if (view_manager->mode == view_mode) {
return true;
}
if (!view_manager->server->ready) {
view_manager->mode = view_mode;
return true;
}
if (view_manager->mode && view_manager->mode->impl->view_mode_leave) {
view_manager->mode->impl->view_mode_leave();
}
view_manager->mode = view_mode;
if (view_manager->mode->impl->view_mode_enter) {
view_manager->mode->impl->view_mode_enter();
}
return true;
}
void view_manager_set_effect_options_impl(action_effect_options_adjust_func_t impl)
{
view_manager->impl.action_effect_options_adjust = impl;
}
void view_manager_adjust_effect_options(enum effect_action action,
struct action_effect_options *options, void *user_data)
{
if (!view_manager->impl.action_effect_options_adjust) {
return;
}
view_manager->impl.action_effect_options_adjust(ACTION_EFFECT_OPTIONS_SURFACE, action, options,
user_data);
}
bool view_has_descendant(struct view *view, struct view *descendant)
{
if (!descendant) {
return !wl_list_empty(&view->children);
}
struct view *tmp;
wl_list_for_each(tmp, &view->children, parent_link) {
if (tmp == descendant) {
return true;
}
if (view_has_descendant(tmp, descendant)) {
return true;
}
}
return false;
}
bool view_has_ancestor(struct view *view, struct view *ancestor)
{
if (!view->parent) {
return false;
}
if (!ancestor) {
return !!view->parent;
}
if (view->parent == ancestor) {
return true;
}
return view_has_ancestor(view->parent, ancestor);
}
void view_move_to_center(struct view *view)
{
struct output *output = output_from_kywc_output(view->output);
int x_center = output->geometry.x + output->geometry.width / 2;
int y_center = output->geometry.y + output->geometry.height / 2;
int x = x_center - view->base.geometry.width / 2;
int y = y_center - view->base.geometry.height / 2;
view_do_move(view, x, y);
}
bool view_validate_move_or_resize_request(struct view *view, struct seat *seat)
{
bool left_button_pressed =
LEFT_BUTTON_PRESSED(seat->cursor->last_click_button, seat->cursor->last_click_pressed);
bool pointer_touch_id = seat->cursor->pointer_touch_id >= 0;
bool tablet_tool_tip_down = seat->cursor->tablet_tool_tip_down;
/**
* Check for touch or tablet, then check for mouse.
*
* The touch device first clicks the SSD area, records the mouse state,
* and triggers a grab. After grabbing, it does not update. At this point,
* if using a touch device to click the surface, the mouse state remains as before.
*/
struct ky_scene_node *node = NULL;
if (pointer_touch_id || tablet_tool_tip_down) {
node = seat->cursor->hover.node;
} else if (left_button_pressed) {
node = seat->cursor->focus.node;
}
return node && wlr_surface_try_from_node(node) == view->surface;
}
struct view_mode *view_manager_mode_register(const struct view_mode_interface *impl)
{
struct view_mode *mode = malloc(sizeof(*mode));
if (!mode) {
return NULL;
}
wl_list_insert(&view_manager->view_modes, &mode->link);
mode->impl = impl;
return mode;
}
void view_manager_mode_unregister(struct view_mode *mode)
{
wl_list_remove(&mode->link);
free(mode);
}
void view_manager_for_each_view(view_iterator_func_t iterator, bool mapped, void *data)
{
struct view *view;
wl_list_for_each(view, &view_manager->views, link) {
if (view->base.mapped == mapped) {
if (iterator(&view->base, data)) {
break;
}
}
}
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&view_manager->display_destroy.link);
wl_list_remove(&view_manager->new_xdg_toplevel.link);
}
static void xdg_foreign_create(struct server *server)
{
struct wlr_xdg_foreign_registry *foreign_registry =
wlr_xdg_foreign_registry_create(server->display);
wlr_xdg_foreign_v1_create(server->display, foreign_registry);
wlr_xdg_foreign_v2_create(server->display, foreign_registry);
}
struct view_manager *view_manager_create(struct server *server)
{
view_manager = calloc(1, sizeof(*view_manager));
if (!view_manager) {
return NULL;
}
view_manager->server = server;
wl_list_init(&view_manager->views);
wl_signal_init(&view_manager->events.new_view);
wl_signal_init(&view_manager->events.new_mapped_view);
wl_signal_init(&view_manager->events.show_desktop);
wl_signal_init(&view_manager->events.activate_view);
wl_signal_init(&view_manager->events.highlight);
view_manager->theme_update.notify = handle_theme_update;
wl_list_init(&view_manager->theme_update.link);
view_manager->theme_icon_update.notify = handle_theme_icon_update;
wl_list_init(&view_manager->theme_icon_update.link);
view_manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &view_manager->display_destroy);
view_manager->server_terminate.notify = handle_server_terminate;
wl_signal_add(&server->events.terminate, &view_manager->server_terminate);
view_manager->server_ready.notify = handle_server_ready;
wl_signal_add(&server->events.ready, &view_manager->server_ready);
view_manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &view_manager->server_destroy);
view_manager->activated.minimize.notify = handle_activated_view_minimized;
view_manager->activated.unmap.notify = handle_activated_view_unmap;
/* create all layers */
for (int layer = LAYER_FIRST; layer < LAYER_NUMBER; layer++) {
view_manager->layers[layer].layer = layer;
view_manager->layers[layer].tree = ky_scene_tree_create(&server->scene->tree);
view_manager->layers[layer].tree->node.role.type = KY_SCENE_ROLE_LAYER;
view_manager->layers[layer].tree->node.role.data = &view_manager->layers[layer];
}
wl_list_init(&view_manager->view_modes);
view_manager_config_init(view_manager);
view_manager->state.num_workspaces = 4;
view_manager->state.view_adsorption = VIEW_ADSORPTION_ALL;
view_manager->state.resize_filter[0] = 10;
view_manager->state.resize_filter[1] = 40;
view_manager->state.configure_timeout[0] = 200;
view_manager->state.configure_timeout[1] = 400;
view_read_config(view_manager);
workspace_manager_create(view_manager);
decoration_manager_create(view_manager);
server_decoration_manager_create(view_manager);
positioner_manager_create(view_manager);
tile_manager_create(view_manager);
window_actions_create(view_manager);
view_manager_actions_create(view_manager);
wl_list_init(&view_manager->new_xdg_toplevel.link);
xdg_shell_init(view_manager);
ukui_startup_management_create(view_manager);
modal_manager_create(server);
wlr_layer_shell_manager_create(server);
wlr_foreign_toplevel_manager_create(server);
ky_toplevel_manager_create(server);
xdg_toplevel_icon_manager_create(server);
kde_plasma_shell_create(server);
kde_plasma_window_management_create(server);
kde_blur_manager_create(server);
kde_slide_manager_create(server);
xdg_dialog_create(server);
xdg_activation_create(server);
ukui_shell_create(server);
ukui_window_management_create(server);
ukui_blur_manager_create(server);
xdg_foreign_create(server);
return view_manager;
}
kylin-wayland-compositor/src/view/view_p.h 0000664 0001750 0001750 00000024143 15160461067 017674 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#ifndef _VIEW_P_H_
#define _VIEW_P_H_
#include
#include "server.h"
#include "view/view.h"
#include "view/workspace.h"
struct view_mode_interface {
const char *name;
void (*view_map)(struct view *view);
void (*view_unmap)(struct view *view);
void (*view_request_move)(struct view *view, int x, int y);
void (*view_request_resize)(struct view *view, struct kywc_box *geometry);
void (*view_request_minimized)(struct view *view, bool minimized);
void (*view_request_maximized)(struct view *view, bool maximized,
struct kywc_output *kywc_output);
void (*view_request_fullscreen)(struct view *view, bool fullscreen,
struct kywc_output *kywc_output);
void (*view_request_tiled)(struct view *view, enum kywc_tile tile,
struct kywc_output *kywc_output);
void (*view_request_activate)(struct view *view);
void (*view_request_show_menu)(struct view *view, struct seat *seat, int x, int y);
void (*view_request_show_tile_flyout)(struct view *view, struct seat *seat,
struct kywc_box *box);
void (*view_request_show_tile_linkage_bar)(struct view *view, uint32_t edges);
void (*view_click)(struct seat *seat, struct view *view, uint32_t button, bool pressed,
enum click_state state);
void (*view_hover)(struct seat *seat, struct view *view);
void (*view_mode_enter)(void);
void (*view_mode_leave)(void);
void (*mode_destroy)(void);
};
enum minimize_effect_type {
MINIMIZE_EFFECT_TYPE_SCALE,
MINIMIZE_EFFECT_TYPE_MAGIC_LAMP,
MINIMIZE_EFFECT_TYPE_NUM,
};
struct view_mode {
struct wl_list link;
const struct view_mode_interface *impl;
};
struct view_manager_impl {
void (*get_startup_geometry)(struct view *view, void *data);
void (*set_tiled)(struct view *view, enum kywc_tile tile, struct kywc_output *kywc_output);
void (*get_tiled_geometry)(struct view *view, struct kywc_box *geometry,
struct kywc_output *kywc_output, enum kywc_tile tile);
void (*fix_geometry)(struct view *view, struct kywc_box *geo, struct kywc_output *kywc_output);
void (*show_tile_assist)(struct view *view, struct seat *seat, struct kywc_output *kywc_output);
void (*show_hang_window)(struct view *view);
action_effect_options_adjust_func_t action_effect_options_adjust;
};
struct view_manager {
struct server *server;
struct wl_list views;
struct view *global_authentication_view;
struct view *desktop;
struct view *highlight;
struct {
struct wl_signal new_view;
struct wl_signal new_mapped_view;
struct wl_signal show_desktop;
struct wl_signal activate_view;
struct wl_signal highlight;
} events;
struct view_layer layers[LAYER_NUMBER];
// TODO: views keyboard focused when multi-seat
struct {
/* only one activated view in all workspaces at once */
struct view *view;
struct wl_listener minimize;
struct wl_listener unmap;
} activated;
struct config *config;
struct view_manager_impl impl;
struct {
uint32_t num_workspaces;
const char *workspace_names[MAX_WORKSPACES];
uint32_t view_adsorption;
bool csd_round_corner;
enum minimize_effect_type minimize_effect_type;
/* 0 is wayland resize_filter. 1 is xwayland resize_filter */
uint32_t resize_filter[2];
uint32_t configure_timeout[2];
} state;
struct wl_listener theme_update;
struct wl_listener theme_icon_update;
struct wl_listener new_xdg_toplevel;
struct wl_listener display_destroy;
struct wl_listener server_terminate;
struct wl_listener server_ready;
struct wl_listener server_destroy;
bool show_desktop_enabled;
bool show_activated_only_enabled;
bool switcher_shown;
struct wl_list view_modes; // struct view_mode.link
struct view_mode *mode;
};
bool view_manager_config_init(struct view_manager *view_manager);
void view_manager_set_switcher_shown(bool shown);
bool view_read_config(struct view_manager *view_manager);
void view_write_config(struct view_manager *view_manager);
void view_close_popups(struct view *view);
void view_update_round_corner(struct view *view);
bool xdg_shell_init(struct view_manager *view_manager);
struct wlr_xdg_surface;
void xdg_view_add_icon_buffer(struct wlr_xdg_surface *wlr_xdg_surface, int scale,
struct wlr_buffer *wlr_buffer);
void xdg_view_clear_icon_buffer(struct wlr_xdg_surface *wlr_xdg_surface);
bool decoration_manager_create(struct view_manager *view_manager);
void view_proxy_destroy(struct view_proxy *view_proxy);
void view_set_current_proxy(struct view *view, struct view_proxy *view_proxy);
struct view_proxy *view_proxy_by_workspace(struct view *view, struct workspace *workspace);
bool positioner_manager_create(struct view_manager *view_manager);
void positioner_add_new_view(struct view *view);
bool server_decoration_manager_create(struct view_manager *view_manager);
bool window_actions_create(struct view_manager *view_manager);
bool view_manager_actions_create(struct view_manager *view_manager);
bool window_menu_manager_create(struct view_manager *view_manager);
void window_menu_show(struct view *view, struct seat *seat, int x, int y);
bool maximize_switcher_create(struct view_manager *view_manager);
void global_authentication_create(struct view *view);
void view_manager_set_global_authentication(struct view *view);
struct view *view_manager_get_global_authentication(void);
bool tile_manager_create(struct view_manager *view_manager);
void view_manager_show_tile_assist(struct view *view, struct seat *seat,
struct kywc_output *kywc_output);
void tile_linkage_bar_show(struct view *view, uint32_t edges);
bool tile_linkage_view_resize(struct view *view, uint32_t edges, double l_x, double l_y);
void tile_linkage_resize_done(struct view *view, bool cancel);
bool tile_flyout_manager_create(struct view_manager *view_manager);
void tile_flyout_show(struct view *view, struct seat *seat, struct kywc_box *box);
struct view *view_manager_get_highlight(void);
void view_manager_add_highlight_view_listener(struct wl_listener *listener);
void highlight_view(struct view *view, bool enable);
void view_fix_geometry(struct view *view, struct kywc_box *geo, struct kywc_box *src_box,
struct kywc_box *dst_box);
struct wlr_xdg_popup;
void xdg_popup_create(struct wlr_xdg_popup *wlr_xdg_popup, struct ky_scene_tree *shell,
struct view_layer *layer, bool use_usable_area);
bool ky_workspace_manager_create(struct server *server);
bool ky_toplevel_manager_create(struct server *server);
bool modal_manager_create(struct server *server);
bool xdg_dialog_create(struct server *server);
bool xdg_activation_create(struct server *server);
struct view_mode *view_manager_mode_from_name(const char *name);
struct view_mode *view_manager_mode_register(const struct view_mode_interface *impl);
void view_manager_mode_unregister(struct view_mode *mode);
void stack_mode_register(struct view_manager *view_manager);
void tablet_mode_register(struct view_manager *view_manager);
#if HAVE_KDE_VIRTUAL_DESKTOP
bool kde_virtual_desktop_management_create(struct server *server);
#else
static __attribute__((unused)) inline bool
kde_virtual_desktop_management_create(struct server *server)
{
return false;
}
#endif
#if HAVE_WLR_FOREIGN_TOPLEVEL
bool wlr_foreign_toplevel_manager_create(struct server *server);
#else
static __attribute__((unused)) inline bool
wlr_foreign_toplevel_manager_create(struct server *server)
{
return false;
}
#endif
#if HAVE_WLR_LAYER_SHELL
bool wlr_layer_shell_manager_create(struct server *server);
#else
static __attribute__((unused)) inline bool wlr_layer_shell_manager_create(struct server *server)
{
return false;
}
#endif
#if HAVE_KDE_PLASMA_SHELL
bool kde_plasma_shell_create(struct server *server);
#else
static __attribute__((unused)) inline bool kde_plasma_shell_create(struct server *server)
{
return false;
}
#endif
#if HAVE_KDE_PLASMA_WINDOW_MANAGEMENT
bool kde_plasma_window_management_create(struct server *server);
#else
static __attribute__((unused)) inline bool
kde_plasma_window_management_create(struct server *server)
{
return false;
}
#endif
#if HAVE_KDE_BLUR
bool kde_blur_manager_create(struct server *server);
#else
static __attribute__((unused)) inline bool kde_blur_manager_create(struct server *server)
{
return false;
}
#endif
#if HAVE_KDE_SLIDE
bool kde_slide_manager_create(struct server *server);
#else
static __attribute__((unused)) inline bool kde_slide_manager_create(struct server *server)
{
return false;
}
#endif
#if HAVE_UKUI_SHELL
bool ukui_shell_create(struct server *server);
uint32_t ukui_shell_get_surface_decoration_flags(struct wlr_surface *surface);
#else
static __attribute__((unused)) inline bool ukui_shell_create(struct server *server)
{
return false;
}
static __attribute__((unused)) inline uint32_t
ukui_shell_get_surface_decoration_flags(struct wlr_surface *surface)
{
return 0;
}
#endif
#if HAVE_UKUI_WINDOW_MANAGEMENT
bool ukui_window_management_create(struct server *server);
#else
static __attribute__((unused)) inline bool ukui_window_management_create(struct server *server)
{
return false;
}
#endif
#if HAVE_UKUI_BLUR
bool ukui_blur_manager_create(struct server *server);
#else
static __attribute__((unused)) inline bool ukui_blur_manager_create(struct server *server)
{
return false;
}
#endif
#if HAVE_UKUI_STARTUP
bool ukui_startup_management_create(struct view_manager *view_manager);
#else
static __attribute__((unused)) inline bool
ukui_startup_management_create(struct view_manager *view_manager)
{
return false;
}
#endif
#if HAVE_XDG_TOPLEVEL_ICON
bool xdg_toplevel_icon_manager_create(struct server *server);
#else
static __attribute__((unused)) inline bool xdg_toplevel_icon_manager_create(struct server *server)
{
return false;
}
#endif
#endif /* _VIEW_P_H_ */
kylin-wayland-compositor/src/view/decoration.c 0000664 0001750 0001750 00000024124 15160461067 020524 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include "view_p.h"
struct decoration_manager {
struct wl_list decos;
struct wl_listener xdg_toplevel_decoration;
struct wl_listener server_decoration;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct decoration {
struct wl_list link;
struct wlr_surface *surface;
struct wlr_server_decoration *server_deco;
struct wl_listener server_deco_apply_mode;
struct wl_listener server_deco_destroy;
struct wlr_xdg_toplevel_decoration_v1 *xdg_deco;
struct wl_listener xdg_deco_request_mode;
struct wl_listener xdg_deco_destroy;
struct wl_listener surface_commit;
struct view *view;
bool should_use_ssd;
};
static struct decoration_manager *manager = NULL;
static void decoration_apply(struct decoration *deco)
{
if (!deco->view) {
return;
}
enum kywc_ssd ssd = deco->should_use_ssd ? KYWC_SSD_ALL : KYWC_SSD_NONE;
if (ssd == KYWC_SSD_ALL && deco->view->base.ssd != KYWC_SSD_NONE) {
ssd = deco->view->base.ssd;
}
view_set_decoration(deco->view, ssd);
}
static struct decoration *decoration_from_surface(struct wlr_surface *surface)
{
struct decoration *deco;
wl_list_for_each(deco, &manager->decos, link) {
if (deco->surface == surface) {
return deco;
}
}
deco = calloc(1, sizeof(*deco));
if (!deco) {
return NULL;
}
deco->surface = surface;
deco->view = view_try_from_wlr_surface(surface);
wl_list_insert(&manager->decos, &deco->link);
return deco;
}
static void decoration_consider_destroy(struct decoration *deco)
{
if (deco->xdg_deco || deco->server_deco) {
return;
}
wl_list_remove(&deco->link);
free(deco);
}
static void handle_server_deco_destroy(struct wl_listener *listener, void *data)
{
struct decoration *deco = wl_container_of(listener, deco, server_deco_destroy);
wl_list_remove(&deco->server_deco_destroy.link);
wl_list_remove(&deco->server_deco_apply_mode.link);
deco->server_deco = NULL;
decoration_consider_destroy(deco);
}
static void handle_xdg_deco_destroy(struct wl_listener *listener, void *data)
{
struct decoration *deco = wl_container_of(listener, deco, xdg_deco_destroy);
wl_list_remove(&deco->xdg_deco_destroy.link);
wl_list_remove(&deco->xdg_deco_request_mode.link);
wl_list_remove(&deco->surface_commit.link);
deco->xdg_deco = NULL;
decoration_consider_destroy(deco);
}
static void handle_surface_commit(struct wl_listener *listener, void *data)
{
struct decoration *deco = wl_container_of(listener, deco, surface_commit);
if (!deco->xdg_deco->toplevel->base->initial_commit) {
return;
}
enum wlr_xdg_toplevel_decoration_v1_mode xdg_mode =
deco->should_use_ssd ? WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
: WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
wlr_xdg_toplevel_decoration_v1_set_mode(deco->xdg_deco, xdg_mode);
wl_list_remove(&deco->surface_commit.link);
wl_list_init(&deco->surface_commit.link);
deco->surface_commit.notify = NULL;
}
static void xdg_toplevel_decoration_set_mode(struct decoration *deco,
struct wlr_xdg_toplevel_decoration_v1 *xdg_deco)
{
if (xdg_deco->toplevel->base->initialized) {
enum wlr_xdg_toplevel_decoration_v1_mode xdg_mode =
deco->should_use_ssd ? WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
: WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
wlr_xdg_toplevel_decoration_v1_set_mode(xdg_deco, xdg_mode);
return;
}
// set in surface initial commit
if (!deco->surface_commit.notify) {
deco->surface_commit.notify = handle_surface_commit;
wl_signal_add(&deco->surface->events.commit, &deco->surface_commit);
}
}
static void handle_xdg_deco_request_mode(struct wl_listener *listener, void *data)
{
struct decoration *deco = wl_container_of(listener, deco, xdg_deco_request_mode);
struct wlr_xdg_toplevel_decoration_v1 *wlr_xdg_decoration = data;
enum wlr_xdg_toplevel_decoration_v1_mode mode = wlr_xdg_decoration->requested_mode;
if (mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_NONE) {
mode = ((deco->server_deco || deco->xdg_deco) && !deco->should_use_ssd)
? WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE
: WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
}
kywc_log(KYWC_DEBUG, "Surface %p xdg-decoration mode is %d", deco->surface, mode);
deco->should_use_ssd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
xdg_toplevel_decoration_set_mode(deco, wlr_xdg_decoration);
if (deco->server_deco) {
uint32_t server_mode = deco->should_use_ssd ? WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
: WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
if (deco->server_deco->mode != server_mode) {
deco->server_deco->mode = server_mode;
/* org_kde_kwin_server_decoration_send_mode */
wl_resource_post_event(deco->server_deco->resource, 0, server_mode);
}
}
decoration_apply(deco);
}
static void xdg_toplevel_decoration(struct wl_listener *listener, void *data)
{
struct wlr_xdg_toplevel_decoration_v1 *wlr_xdg_decoration = data;
struct wlr_surface *surface = wlr_xdg_decoration->toplevel->base->surface;
struct decoration *deco = decoration_from_surface(surface);
if (!deco) {
return;
}
deco->xdg_deco_destroy.notify = handle_xdg_deco_destroy;
wl_signal_add(&wlr_xdg_decoration->events.destroy, &deco->xdg_deco_destroy);
deco->xdg_deco_request_mode.notify = handle_xdg_deco_request_mode;
wl_signal_add(&wlr_xdg_decoration->events.request_mode, &deco->xdg_deco_request_mode);
wl_list_init(&deco->surface_commit.link);
handle_xdg_deco_request_mode(&deco->xdg_deco_request_mode, wlr_xdg_decoration);
deco->xdg_deco = wlr_xdg_decoration;
}
static void handle_server_deco_apply_mode(struct wl_listener *listener, void *data)
{
struct decoration *deco = wl_container_of(listener, deco, server_deco_apply_mode);
struct wlr_server_decoration *wlr_server_decoration = data;
uint32_t mode = wlr_server_decoration->mode;
kywc_log(KYWC_DEBUG, "Surface %p server decoration mode is %d", deco->surface, mode);
deco->should_use_ssd = mode == WLR_SERVER_DECORATION_MANAGER_MODE_SERVER;
if (deco->xdg_deco) {
xdg_toplevel_decoration_set_mode(deco, deco->xdg_deco);
}
decoration_apply(deco);
}
static void server_decoration(struct wl_listener *listener, void *data)
{
struct wlr_server_decoration *wlr_server_decoration = data;
struct wlr_surface *surface = wlr_server_decoration->surface;
struct decoration *deco = decoration_from_surface(surface);
if (!deco) {
return;
}
/* multi decoration for one surface, use the last one */
if (deco->server_deco) {
wl_list_remove(&deco->server_deco_destroy.link);
wl_list_remove(&deco->server_deco_apply_mode.link);
}
deco->server_deco_destroy.notify = handle_server_deco_destroy;
wl_signal_add(&wlr_server_decoration->events.destroy, &deco->server_deco_destroy);
deco->server_deco_apply_mode.notify = handle_server_deco_apply_mode;
wl_signal_add(&wlr_server_decoration->events.mode, &deco->server_deco_apply_mode);
/* apply current deco type only when first time, may no mode signal */
if (!deco->xdg_deco && !deco->server_deco) {
handle_server_deco_apply_mode(&deco->server_deco_apply_mode, wlr_server_decoration);
}
deco->server_deco = wlr_server_decoration;
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->server_destroy.link);
free(manager);
manager = NULL;
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->display_destroy.link);
wl_list_remove(&manager->xdg_toplevel_decoration.link);
wl_list_remove(&manager->server_decoration.link);
}
bool decoration_manager_create(struct view_manager *view_manager)
{
manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
wl_list_init(&manager->decos);
wl_list_init(&manager->xdg_toplevel_decoration.link);
wl_list_init(&manager->server_decoration.link);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(view_manager->server->display, &manager->display_destroy);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(view_manager->server, &manager->server_destroy);
struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager =
wlr_xdg_decoration_manager_v1_create(view_manager->server->display);
if (!xdg_decoration_manager) {
kywc_log(KYWC_ERROR, "Unable to create the XDG deco manager");
} else {
manager->xdg_toplevel_decoration.notify = xdg_toplevel_decoration;
wl_signal_add(&xdg_decoration_manager->events.new_toplevel_decoration,
&manager->xdg_toplevel_decoration);
}
struct wlr_server_decoration_manager *server_decoration_manager =
wlr_server_decoration_manager_create(view_manager->server->display);
if (!server_decoration_manager) {
kywc_log(KYWC_ERROR, "Unable to create the server deco manager");
} else {
uint32_t default_mode = WLR_SERVER_DECORATION_MANAGER_MODE_SERVER;
wlr_server_decoration_manager_set_default_mode(server_decoration_manager, default_mode);
manager->server_decoration.notify = server_decoration;
wl_signal_add(&server_decoration_manager->events.new_decoration,
&manager->server_decoration);
}
/* oops, all decoration manager create failed */
if (!xdg_decoration_manager && !server_decoration_manager) {
free(manager);
return false;
}
return true;
}
kylin-wayland-compositor/src/view/kde_plasma_shell.c 0000664 0001750 0001750 00000035156 15160461067 021673 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include "input/cursor.h"
#include "input/seat.h"
#include "plasma-shell-protocol.h"
#include "scene/surface.h"
#include "util/wayland.h"
#include "view_p.h"
#define PLASMA_SHELL_VERSION 8
struct kde_plasma_shell {
struct wl_global *global;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct kde_plasma_surface {
struct wlr_surface *wlr_surface;
struct wl_listener surface_map;
struct wl_listener surface_destroy;
/* for popup position */
struct ky_scene_buffer *buffer;
/* get in map listener */
struct view *view;
bool takes_focus;
struct wl_listener view_map;
struct wl_listener view_unmap;
struct wl_listener view_destroy;
struct wl_listener view_update_capabilities;
int32_t x, y;
enum org_kde_plasma_surface_role role;
int32_t skip_taskbar, skip_switcher;
bool open_under_cursor;
};
static void handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void handle_set_output(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *output)
{
// Not implemented yet
}
static void kde_plasma_surface_apply_position(struct kde_plasma_surface *surface)
{
if (surface->view) {
view_do_move(surface->view, surface->x, surface->y);
} else if (surface->buffer) {
struct ky_scene_node *node = &surface->buffer->node;
int lx, ly;
ky_scene_node_coords(node, &lx, &ly);
ky_scene_node_set_position(&surface->buffer->node, node->x + surface->x - lx,
node->y + surface->y - ly);
}
}
static void handle_set_position(struct wl_client *client, struct wl_resource *resource, int32_t x,
int32_t y)
{
struct kde_plasma_surface *surface = wl_resource_get_user_data(resource);
if (!surface->wlr_surface) {
return;
}
surface->x = x;
surface->y = y;
kde_plasma_surface_apply_position(surface);
}
static enum kywc_view_role role_from_plasma_surface(struct kde_plasma_surface *surface)
{
switch (surface->role) {
default:
return KYWC_VIEW_ROLE_NORMAL;
case ORG_KDE_PLASMA_SURFACE_ROLE_DESKTOP:
return KYWC_VIEW_ROLE_DESKTOP;
case ORG_KDE_PLASMA_SURFACE_ROLE_PANEL:
return KYWC_VIEW_ROLE_PANEL;
case ORG_KDE_PLASMA_SURFACE_ROLE_TOOLTIP:
return KYWC_VIEW_ROLE_TOOLTIP;
case ORG_KDE_PLASMA_SURFACE_ROLE_ONSCREENDISPLAY:
return KYWC_VIEW_ROLE_ONSCREENDISPLAY;
case ORG_KDE_PLASMA_SURFACE_ROLE_NOTIFICATION:
return KYWC_VIEW_ROLE_NOTIFICATION;
case ORG_KDE_PLASMA_SURFACE_ROLE_CRITICALNOTIFICATION:
return KYWC_VIEW_ROLE_CRITICALNOTIFICATION;
case ORG_KDE_PLASMA_SURFACE_ROLE_APPLETPOPUP:
return KYWC_VIEW_ROLE_APPLETPOPUP;
}
}
static void handle_set_role(struct wl_client *client, struct wl_resource *resource, uint32_t role)
{
struct kde_plasma_surface *surface = wl_resource_get_user_data(resource);
if (!surface->wlr_surface) {
return;
}
if (surface->view) {
kywc_log(KYWC_WARN, "Plasma-shell: reject set_role(%d) request after mapped from %s", role,
surface->view->base.app_id);
return;
}
surface->role = role;
}
static void handle_set_panel_behavior(struct wl_client *client, struct wl_resource *resource,
uint32_t flag)
{
// Not implemented yet
}
static void handle_set_skip_taskbar(struct wl_client *client, struct wl_resource *resource,
uint32_t skip)
{
struct kde_plasma_surface *surface = wl_resource_get_user_data(resource);
if (!surface->wlr_surface) {
return;
}
surface->skip_taskbar = skip;
if (surface->view) {
kywc_view_set_skip_taskbar(&surface->view->base, skip);
}
}
static void handle_panel_auto_hide_hide(struct wl_client *client, struct wl_resource *resource)
{
// Not implemented yet
}
static void handle_panel_auto_hide_show(struct wl_client *client, struct wl_resource *resource)
{
// Not implemented yet
}
static void handle_set_panel_takes_focus(struct wl_client *client, struct wl_resource *resource,
uint32_t takes_focus)
{
struct kde_plasma_surface *surface = wl_resource_get_user_data(resource);
if (!surface->wlr_surface || !surface->view) {
return;
}
struct kywc_view *kywc_view = &surface->view->base;
if (kywc_view->role == KYWC_VIEW_ROLE_PANEL && surface->takes_focus != takes_focus) {
surface->takes_focus = takes_focus;
view_update_capabilities(surface->view, KYWC_VIEW_FOCUSABLE | KYWC_VIEW_ACTIVATABLE);
}
}
static void handle_set_skip_switcher(struct wl_client *client, struct wl_resource *resource,
uint32_t skip)
{
struct kde_plasma_surface *surface = wl_resource_get_user_data(resource);
if (!surface->wlr_surface) {
return;
}
surface->skip_switcher = skip;
if (surface->view) {
kywc_view_set_skip_switcher(&surface->view->base, skip);
}
}
static void handle_open_under_cursor(struct wl_client *client, struct wl_resource *resource)
{
struct kde_plasma_surface *surface = wl_resource_get_user_data(resource);
if (!surface->wlr_surface) {
return;
}
surface->open_under_cursor = true;
if (surface->view) {
struct seat *seat = surface->view->base.focused_seat;
view_do_move(surface->view, seat->cursor->lx, seat->cursor->ly);
}
}
static const struct org_kde_plasma_surface_interface kde_plasma_surface_impl = {
.destroy = handle_destroy,
.set_output = handle_set_output,
.set_position = handle_set_position,
.set_role = handle_set_role,
.set_panel_behavior = handle_set_panel_behavior,
.set_skip_taskbar = handle_set_skip_taskbar,
.panel_auto_hide_hide = handle_panel_auto_hide_hide,
.panel_auto_hide_show = handle_panel_auto_hide_show,
.set_panel_takes_focus = handle_set_panel_takes_focus,
.set_skip_switcher = handle_set_skip_switcher,
.open_under_cursor = handle_open_under_cursor,
};
static void kde_plasma_surface_set_usable_area(struct kde_plasma_surface *surface, bool enabled)
{
bool has_area =
enabled && surface->view->base.mapped && surface->view->base.role == KYWC_VIEW_ROLE_PANEL;
view_set_exclusive(surface->view, has_area);
}
static void surface_handle_view_map(struct wl_listener *listener, void *data)
{
struct kde_plasma_surface *surface = wl_container_of(listener, surface, view_map);
kde_plasma_surface_set_usable_area(surface, true);
}
static void surface_handle_view_unmap(struct wl_listener *listener, void *data)
{
struct kde_plasma_surface *surface = wl_container_of(listener, surface, view_unmap);
kde_plasma_surface_set_usable_area(surface, false);
}
static void surface_handle_view_destroy(struct wl_listener *listener, void *data)
{
struct kde_plasma_surface *surface = wl_container_of(listener, surface, view_destroy);
wl_list_remove(&surface->view_destroy.link);
wl_list_remove(&surface->view_map.link);
wl_list_remove(&surface->view_unmap.link);
wl_list_remove(&surface->view_update_capabilities.link);
surface->view = NULL;
}
static void surface_handle_view_update_capabilities(struct wl_listener *listener, void *data)
{
struct kde_plasma_surface *surface =
wl_container_of(listener, surface, view_update_capabilities);
struct view_update_capabilities_event *event = data;
if (event->mask & KYWC_VIEW_FOCUSABLE) {
if (!surface->takes_focus) {
event->state &= ~KYWC_VIEW_FOCUSABLE;
}
}
if (event->mask & KYWC_VIEW_ACTIVATABLE) {
if (!surface->takes_focus) {
event->state &= ~KYWC_VIEW_ACTIVATABLE;
}
}
}
static void surface_handle_map(struct wl_listener *listener, void *data)
{
struct kde_plasma_surface *surface = wl_container_of(listener, surface, surface_map);
/* useless once we connect surface and view */
wl_list_remove(&surface->surface_map.link);
wl_list_init(&surface->surface_map.link);
/* get view from surface */
if (!surface->view) {
surface->view = view_try_from_wlr_surface(surface->wlr_surface);
if (!surface->view) {
kywc_log(KYWC_DEBUG, "Surface is not a toplevel");
surface->buffer = ky_scene_buffer_try_from_surface(surface->wlr_surface);
return;
}
wl_signal_add(&surface->view->base.events.destroy, &surface->view_destroy);
view_add_update_capabilities_listener(surface->view, &surface->view_update_capabilities);
}
if (surface->skip_taskbar != -1) {
kywc_view_set_skip_taskbar(&surface->view->base, surface->skip_taskbar);
}
if (surface->skip_switcher != -1) {
kywc_view_set_skip_switcher(&surface->view->base, surface->skip_switcher);
}
if (surface->role != UINT32_MAX) {
view_set_role(surface->view, role_from_plasma_surface(surface));
}
if (surface->open_under_cursor) {
struct seat *seat = surface->view->base.focused_seat;
surface->view->base.has_initial_position = true;
view_do_move(surface->view, seat->cursor->lx, seat->cursor->ly);
}
/* apply set_position called before map */
if (surface->x != INT32_MAX || surface->y != INT32_MAX) {
surface->view->base.has_initial_position = true;
kde_plasma_surface_apply_position(surface);
}
surface->view_map.notify = surface_handle_view_map;
wl_signal_add(&surface->view->base.events.map, &surface->view_map);
surface->view_unmap.notify = surface_handle_view_unmap;
wl_signal_add(&surface->view->base.events.unmap, &surface->view_unmap);
/* workaround to fix this listener is behind view map */
if (surface->view->base.mapped) {
surface_handle_view_map(&surface->view_map, NULL);
}
}
static void surface_handle_destroy(struct wl_listener *listener, void *data)
{
struct kde_plasma_surface *surface = wl_container_of(listener, surface, surface_destroy);
wl_list_remove(&surface->surface_map.link);
wl_list_remove(&surface->surface_destroy.link);
surface->wlr_surface = NULL;
}
static void kde_plasma_surface_handle_resource_destroy(struct wl_resource *resource)
{
struct kde_plasma_surface *surface = wl_resource_get_user_data(resource);
if (surface->wlr_surface) {
surface_handle_destroy(&surface->surface_destroy, NULL);
}
if (surface->view) {
if (surface->view->base.mapped) {
surface_handle_view_unmap(&surface->view_unmap, NULL);
}
surface_handle_view_destroy(&surface->view_destroy, NULL);
}
free(surface);
}
static void handle_get_surface(struct wl_client *client, struct wl_resource *shell_resource,
uint32_t id, struct wl_resource *surface_resource)
{
struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource);
/* create a plasma surface */
struct kde_plasma_surface *surface = calloc(1, sizeof(*surface));
if (!surface) {
wl_client_post_no_memory(client);
return;
}
int version = wl_resource_get_version(shell_resource);
struct wl_resource *resource =
wl_resource_create(client, &org_kde_plasma_surface_interface, version, id);
if (!resource) {
free(surface);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &kde_plasma_surface_impl, surface,
kde_plasma_surface_handle_resource_destroy);
surface->x = surface->y = INT32_MAX;
surface->role = UINT32_MAX;
surface->skip_taskbar = surface->skip_switcher = -1;
surface->takes_focus = true;
surface->wlr_surface = wlr_surface;
surface->surface_map.notify = surface_handle_map;
wl_signal_add_next(&wlr_surface->events.map, &surface->surface_map);
surface->surface_destroy.notify = surface_handle_destroy;
wl_signal_add(&wlr_surface->events.destroy, &surface->surface_destroy);
surface->view = view_try_from_wlr_surface(surface->wlr_surface);
surface->view_destroy.notify = surface_handle_view_destroy;
surface->view_update_capabilities.notify = surface_handle_view_update_capabilities;
if (surface->view) {
wl_list_init(&surface->view_map.link);
wl_list_init(&surface->view_unmap.link);
wl_signal_add(&surface->view->base.events.destroy, &surface->view_destroy);
view_add_update_capabilities_listener(surface->view, &surface->view_update_capabilities);
}
/* workaround to fix this request is behind surface map */
if (surface->wlr_surface->mapped) {
surface_handle_map(&surface->surface_map, NULL);
}
}
static const struct org_kde_plasma_shell_interface kde_plasma_shell_impl = {
.get_surface = handle_get_surface,
};
static void kde_plasma_shell_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &org_kde_plasma_shell_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
struct kde_plasma_shell *shell = data;
wl_resource_set_implementation(resource, &kde_plasma_shell_impl, shell, NULL);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
struct kde_plasma_shell *shell = wl_container_of(listener, shell, display_destroy);
wl_list_remove(&shell->display_destroy.link);
wl_global_destroy(shell->global);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct kde_plasma_shell *shell = wl_container_of(listener, shell, server_destroy);
wl_list_remove(&shell->server_destroy.link);
free(shell);
}
bool kde_plasma_shell_create(struct server *server)
{
struct kde_plasma_shell *shell = calloc(1, sizeof(*shell));
if (!shell) {
return false;
}
shell->global = wl_global_create(server->display, &org_kde_plasma_shell_interface,
PLASMA_SHELL_VERSION, shell, kde_plasma_shell_bind);
if (!shell->global) {
kywc_log(KYWC_WARN, "Kde plasma shell create failed");
free(shell);
return false;
}
shell->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &shell->server_destroy);
shell->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &shell->display_destroy);
return true;
}
kylin-wayland-compositor/src/view/maximize_switcher.c 0000664 0001750 0001750 00000052564 15160461067 022141 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#define _POSIX_C_SOURCE 200809L
#include
#include
#include
#include
#include
#include
#include "input/cursor.h"
#include "input/seat.h"
#include "output.h"
#include "painter.h"
#include "theme.h"
#include "util/macros.h"
#include "view/workspace.h"
#include "view_p.h"
#include "widget/scaled_buffer.h"
#include "widget/widget.h"
#define SELECT_WIDTH_GAP (5)
#define ICON_OPACIPY (0.4)
#define ITEM_HEIGHT (48)
#define MAX_DISPLAY_VIEW (25)
#define MIN_DISPLAY_VIEW (4)
#define MAX_WIDTH_RATIO (0.8)
#define MAX_HEIGHT_RATIO (0.8)
#define MIN_WIDTH_RATIO (0.2)
enum set_dir {
NONE = 0,
BOTTOM,
TOP,
};
struct maximize_switcher_color {
float background_color[4];
float font_color[4];
float select_color[4];
};
static struct maximize_switcher_color light = {
.background_color = { 207.0 / 255.0, 207.0 / 255.0, 207.0 / 255.0, 1.0 },
.font_color = { 0, 0, 0, 1.0 },
.select_color = { 173.0 / 255.0, 216.0 / 255.0, 230.0 / 255.0, 1.0 },
};
static struct maximize_switcher_color dark = {
.background_color = { 54.0 / 255, 54.0 / 255, 54.0 / 255, 1.0 },
.font_color = { 1.0, 1.0, 1.0, 1.0 },
.select_color = { 173.0 / 255.0, 216.0 / 255.0, 230.0 / 255.0, 1.0 },
};
struct item_view {
struct kywc_view *kywc_view;
struct widget *title_text;
struct ky_scene_tree *tree;
struct ky_scene_rect *background;
struct ky_scene_node *icon_node;
struct ky_scene_node *text_node;
int text_width, text_height;
float scale;
char *text;
struct wl_listener view_destroy;
};
static struct maximize_switcher {
struct ky_scene_tree *tree;
struct ky_scene_rect *background;
struct item_view *active;
struct maximize_switcher_color *color;
struct item_view **item_views;
struct seat_pointer_grab pointer_grab;
struct seat_keyboard_grab keyboard_grab;
struct seat_touch_grab touch_grab;
int pending, current;
int width, height;
int max_width, max_height;
/* *
* restrict page display items when the height
* exceeds the maximum height of the background.
*/
int views_control;
int num_windows;
int num_view;
int direction;
int last_position;
bool enable;
float icon_opacity;
struct wlr_output *output;
struct wl_listener output_frame;
struct wl_listener theme_update;
struct wl_listener server_destroy;
} *switcher = NULL;
static struct shortcut {
char *keybind;
char *desc;
} shortcuts[] = {
{ "Alt+m", "traverse all maximized views" },
};
static void maximize_switcher_set_enable(bool enable);
static void ensure_thumbnails_size(int num)
{
if (switcher->num_windows > num) {
return;
}
int alloc = switcher->num_windows ? switcher->num_windows : 16;
while (alloc < num) {
alloc *= 2;
}
if (!switcher->item_views) {
switcher->item_views = malloc(alloc * sizeof(struct item_view *));
} else if (alloc > switcher->num_windows) {
switcher->item_views = realloc(switcher->item_views, alloc * sizeof(struct item_view *));
}
switcher->num_windows = alloc;
}
static bool item_hover(struct seat *seat, struct ky_scene_node *node, double x, double y,
uint32_t time, bool first, bool hold, void *data)
{
return false;
}
static void item_leave(struct seat *seat, struct ky_scene_node *node, bool last, void *data) {}
static void item_click(struct seat *seat, struct ky_scene_node *node, uint32_t button, bool pressed,
uint32_t time, enum click_state state, void *data)
{
/* do actions when released */
if (pressed || button != BTN_LEFT) {
return;
}
struct item_view *item = data;
struct item_view *item_view;
for (int i = 0; i < switcher->num_view; i++) {
item_view = switcher->item_views[i];
if (item == item_view) {
switcher->pending = i;
switcher->direction = NONE;
}
}
output_schedule_frame(switcher->output);
}
static const struct input_event_node_impl item_impl = {
.hover = item_hover,
.leave = item_leave,
.click = item_click,
};
static void pointer_grab_cancel(struct seat_pointer_grab *pointer_grab)
{
maximize_switcher_set_enable(false);
}
static bool pointer_grab_button(struct seat_pointer_grab *pointer_grab, uint32_t time,
uint32_t button, bool pressed)
{
struct maximize_switcher *window_menu = pointer_grab->data;
struct seat *seat = pointer_grab->seat;
/* check current hover node in the window menu tree */
struct input_event_node *inode = input_event_node_from_node(seat->cursor->hover.node);
struct ky_scene_node *node = input_event_node_root(inode);
if (node == &window_menu->tree->node) {
inode->impl->click(seat, seat->cursor->hover.node, button, pressed, time, CLICK_STATE_NONE,
inode->data);
} else if (pressed) {
maximize_switcher_set_enable(false);
}
return true;
}
static bool pointer_grab_motion(struct seat_pointer_grab *pointer_grab, uint32_t time, double lx,
double ly)
{
return false;
}
static bool pointer_grab_axis(struct seat_pointer_grab *pointer_grab, uint32_t time, bool vertical,
double value)
{
return true;
}
static const struct seat_pointer_grab_interface pointer_grab_impl = {
.motion = pointer_grab_motion,
.button = pointer_grab_button,
.axis = pointer_grab_axis,
.cancel = pointer_grab_cancel,
};
static bool touch_grab_touch(struct seat_touch_grab *touch_grab, uint32_t time, bool down)
{
// FIXME: interactive grab end
struct maximize_switcher *maximize_switcher = touch_grab->data;
return pointer_grab_button(&maximize_switcher->pointer_grab, time, BTN_LEFT, down);
}
static bool touch_grab_motion(struct seat_touch_grab *touch_grab, uint32_t time, double lx,
double ly)
{
return false;
}
static void touch_grab_cancel(struct seat_touch_grab *touch_grab)
{
maximize_switcher_set_enable(false);
}
static const struct seat_touch_grab_interface touch_grab_impl = {
.touch = touch_grab_touch,
.motion = touch_grab_motion,
.cancel = touch_grab_cancel,
};
static bool keyboard_grab_key(struct seat_keyboard_grab *keyboard_grab, struct keyboard *keyboard,
uint32_t time, uint32_t key, bool pressed, uint32_t modifiers)
{
if (!pressed) {
if (key != KEY_LEFTALT && key != KEY_RIGHTALT) {
return true;
}
if (switcher->active->kywc_view) {
kywc_view_activate(switcher->active->kywc_view);
view_set_focus(view_from_kywc_view(switcher->active->kywc_view), keyboard_grab->seat);
}
maximize_switcher_set_enable(false);
return true;
}
switch (key) {
case KEY_UP:
switcher->direction = TOP;
switcher->pending--;
break;
case KEY_M:
case KEY_DOWN:
switcher->direction = BOTTOM;
switcher->pending++;
break;
case KEY_ESC:
maximize_switcher_set_enable(false);
break;
}
output_schedule_frame(switcher->output);
return true;
}
static void keyboard_grab_cancel(struct seat_keyboard_grab *keyboard_grab)
{
maximize_switcher_set_enable(false);
}
static const struct seat_keyboard_grab_interface keyboard_grab_impl = {
.key = keyboard_grab_key,
.cancel = keyboard_grab_cancel,
};
static void update_title_text(struct item_view *item_view)
{
struct theme *theme = theme_manager_get_theme();
int select_width_gap = SELECT_WIDTH_GAP * 2;
int max_width = switcher->max_width - theme->button_width - select_width_gap;
widget_set_text(item_view->title_text, item_view->text, JUSTIFY_CENTER,
item_view->kywc_view->minimized ? TEXT_ATTR_SLANT : TEXT_ATTR_NONE);
widget_set_font(item_view->title_text, theme->font_name, theme->font_size);
widget_set_front_color(item_view->title_text, switcher->color->font_color);
widget_set_max_size(item_view->title_text, max_width, ITEM_HEIGHT);
widget_set_auto_resize(item_view->title_text, AUTO_RESIZE_ONLY);
widget_set_enabled(item_view->title_text, true);
widget_update(item_view->title_text, true);
int text_width, text_height;
widget_get_size(item_view->title_text, &text_width, &text_height);
if (text_width > max_width) {
switcher->width = max_width + theme->button_width + select_width_gap;
} else if (text_width > switcher->width - theme->button_width - select_width_gap) {
switcher->width = text_width + theme->button_width + select_width_gap;
}
item_view->text_width = text_width;
item_view->text_height = text_height;
}
static void set_icon_buffer(struct item_view *item_view, float opacity)
{
struct kywc_view *kywc_view = item_view->kywc_view;
struct view *view = view_from_kywc_view(kywc_view);
struct wlr_buffer *buf = view_get_icon_buffer(view, item_view->scale);
if (!buf) {
return;
}
struct ky_scene_buffer *buffer = ky_scene_buffer_from_node(item_view->icon_node);
if (buffer->buffer != buf) {
ky_scene_buffer_set_buffer(buffer, buf);
}
if (buffer->buffer != buf) {
return;
}
ky_scene_buffer_set_opacity(buffer, opacity);
struct theme *theme = theme_manager_get_theme();
ky_scene_buffer_set_dest_size(buffer, theme->icon_size, theme->icon_size);
}
static void update_buffer(struct ky_scene_buffer *buffer, float scale, void *data)
{
struct item_view *item_view = data;
item_view->scale = scale;
/* update scene_buffer with new buffer */
set_icon_buffer(item_view, switcher->icon_opacity);
}
static void destroy_buffer(struct ky_scene_buffer *buffer, void *data)
{
/* buffers are destroyed in theme */
}
static struct ky_scene_node *item_get_root(void *data)
{
return &switcher->tree->node;
}
static void handle_view_destroy(struct wl_listener *listener, void *data)
{
struct item_view *item_view = wl_container_of(listener, item_view, view_destroy);
maximize_switcher_set_enable(false);
}
static void get_maximize_views(int *num_views)
{
struct workspace *workspace = workspace_manager_get_current();
struct kywc_output *kywc_output = kywc_output_get_primary();
float color[4] = { 0 };
struct view_proxy *view_proxy;
wl_list_for_each(view_proxy, &workspace->view_proxies, workspace_link) {
struct view *view = view_proxy->view;
if (!view->base.mapped || (!view->base.maximized && !view->base.fullscreen)) {
continue;
}
struct item_view *item_view = calloc(1, sizeof(*item_view));
if (!item_view) {
continue;
}
item_view->kywc_view = &view->base;
item_view->scale = kywc_output->state.scale;
item_view->view_destroy.notify = handle_view_destroy;
wl_signal_add(&view->base.events.destroy, &item_view->view_destroy);
item_view->tree = ky_scene_tree_create(switcher->tree);
input_event_node_create(&item_view->tree->node, &item_impl, item_get_root, NULL, item_view);
/* create background */
item_view->background = ky_scene_rect_create(item_view->tree, 0, 0, color);
/* create title */
if (view->base.minimized) {
char left_bracket[] = "(";
char right_bracket[] = ")";
int len = strlen(view->base.title) + strlen(left_bracket) + strlen(right_bracket) + 1;
item_view->text = malloc(len * sizeof(char));
sprintf(item_view->text, "%s%s%s", left_bracket, view->base.title, right_bracket);
} else {
item_view->text = strdup(view->base.title);
}
item_view->title_text = widget_create(item_view->tree);
item_view->text_node = ky_scene_node_from_widget(item_view->title_text);
/* create icon */
struct ky_scene_buffer *buf = scaled_buffer_create(
item_view->tree, view->output->state.scale, update_buffer, destroy_buffer, item_view);
item_view->icon_node = &buf->node;
/* update */
set_icon_buffer(item_view, switcher->icon_opacity);
update_title_text(item_view);
/* add item_views */
ensure_thumbnails_size(*num_views + 1);
switcher->item_views[*num_views] = item_view;
*num_views += 1;
}
}
static void set_select_item_view(int index, struct item_view *current, struct item_view *new)
{
if (current) {
float color[4] = { 0 };
ky_scene_rect_set_color(current->background, color);
set_icon_buffer(current, switcher->icon_opacity);
}
ky_scene_rect_set_color(new->background, switcher->color->select_color);
set_icon_buffer(new, 1);
}
static void show_current_page_items(int index)
{
struct theme *theme = theme_manager_get_theme();
int icon_width = theme->button_width;
int select_width_gap = SELECT_WIDTH_GAP;
int icon_x = theme->layout_is_right_to_left ? switcher->width - select_width_gap - icon_width
: select_width_gap;
icon_x += (icon_width - theme->icon_size) / 2;
int icon_y = (ITEM_HEIGHT - theme->icon_size) / 2;
struct item_view *item_view;
for (int i = 0; i < switcher->num_view; i++) {
/* skipped view */
int start_i = i - index;
if (start_i < 0) {
continue;
}
if (i >= switcher->views_control + index) {
break;
}
item_view = switcher->item_views[i];
ky_scene_node_set_enabled(&item_view->tree->node, true);
ky_scene_node_set_position(&item_view->tree->node, 0, start_i * ITEM_HEIGHT);
/* set icon position */
ky_scene_node_set_position(item_view->icon_node, icon_x, icon_y);
/* set text position */
int text_x = theme->layout_is_right_to_left ? 0 : icon_width + select_width_gap;
text_x += (switcher->width - icon_width - item_view->text_width - select_width_gap * 2) / 2;
int text_y = (ITEM_HEIGHT - item_view->text_height) / 2;
ky_scene_node_set_position(item_view->text_node, text_x, text_y);
ky_scene_rect_set_size(item_view->background, switcher->width, ITEM_HEIGHT);
}
}
static void handle_output_frame(struct wl_listener *listener, void *data)
{
int num_view = switcher->num_view;
int pending = switcher->pending;
int i_index = 0;
if (num_view == 1 || pending >= num_view) {
pending = 0;
} else if (pending < 0) {
pending = num_view - 1;
i_index =
pending - switcher->views_control >= 0 ? pending + 1 - switcher->views_control : 0;
} else {
if (switcher->direction == BOTTOM) {
i_index = pending < switcher->last_position + switcher->views_control
? switcher->last_position
: pending + 1 - switcher->views_control;
} else if (switcher->direction == TOP) {
i_index = pending < switcher->last_position ? pending : switcher->last_position;
} else {
i_index = switcher->last_position;
}
}
switcher->last_position = i_index;
/* hide all items */
for (int i = 0; i < switcher->num_view; i++) {
ky_scene_node_set_enabled(&switcher->item_views[i]->tree->node, false);
}
show_current_page_items(i_index);
/* select */
struct item_view *current =
switcher->current >= 0 ? switcher->item_views[switcher->current] : NULL;
set_select_item_view(pending - i_index, current, switcher->item_views[pending]);
switcher->pending = pending;
switcher->current = pending;
switcher->active = switcher->item_views[switcher->current];
}
static void hide_maximize_switcher(void)
{
struct item_view *item_view;
for (int i = 0; i < switcher->num_view; i++) {
item_view = switcher->item_views[i];
wl_list_remove(&item_view->view_destroy.link);
ky_scene_node_destroy(&item_view->tree->node);
free(item_view->text);
free(item_view);
}
free(switcher->item_views);
switcher->item_views = NULL;
switcher->num_windows = 0;
wl_list_remove(&switcher->output_frame.link);
ky_scene_node_set_enabled(&switcher->tree->node, false);
}
static bool show_maximize_switcher(void)
{
struct output *output = input_current_output(input_manager_get_default_seat());
struct kywc_box *usable_area = &output->usable_area;
switcher->max_width = usable_area->width * MAX_WIDTH_RATIO;
switcher->max_height = usable_area->height * MAX_HEIGHT_RATIO;
switcher->width = usable_area->width * MIN_WIDTH_RATIO;
int num_view = 0;
get_maximize_views(&num_view);
if (num_view == 0) {
return false;
}
if (num_view >= MAX_DISPLAY_VIEW) {
switcher->height = ITEM_HEIGHT * MAX_DISPLAY_VIEW;
} else if (num_view <= MIN_DISPLAY_VIEW) {
switcher->height = ITEM_HEIGHT * MIN_DISPLAY_VIEW;
} else {
switcher->height = ITEM_HEIGHT * num_view;
}
if (switcher->height > switcher->max_height) {
int max_num = switcher->max_height / ITEM_HEIGHT;
switcher->views_control = max_num;
switcher->height = max_num * ITEM_HEIGHT;
} else {
switcher->views_control = MAX_DISPLAY_VIEW;
}
switcher->num_view = num_view;
int x = usable_area->x + usable_area->width / 2 - switcher->width / 2;
int y = usable_area->y + usable_area->height / 2 - switcher->height / 2;
ky_scene_rect_set_size(switcher->background, switcher->width, switcher->height);
ky_scene_node_set_position(&switcher->tree->node, x, y);
ky_scene_node_set_enabled(&switcher->tree->node, true);
switcher->output_frame.notify = handle_output_frame;
wl_signal_add(&output->scene_output->events.frame, &switcher->output_frame);
switcher->pending = 1;
switcher->current = -1;
switcher->direction = BOTTOM;
switcher->output = output->wlr_output;
output_schedule_frame(switcher->output);
return true;
}
static void maximize_switcher_set_enable(bool enable)
{
if (switcher->enable == enable) {
return;
}
struct seat *seat = input_manager_get_default_seat();
switcher->enable = enable;
if (!enable) {
hide_maximize_switcher();
seat_end_pointer_grab(seat, &switcher->pointer_grab);
seat_end_keyboard_grab(seat, &switcher->keyboard_grab);
seat_end_touch_grab(seat, &switcher->touch_grab);
return;
}
if (!show_maximize_switcher()) {
switcher->enable = false;
return;
}
seat_start_pointer_grab(seat, &switcher->pointer_grab);
seat_start_keyboard_grab(seat, &switcher->keyboard_grab);
seat_start_touch_grab(seat, &switcher->touch_grab);
}
static void shortcut_action(struct key_binding *binding, void *data)
{
maximize_switcher_set_enable(true);
}
static void switcher_register_shortcuts(void)
{
for (size_t i = 0; i < ARRAY_SIZE(shortcuts); i++) {
struct shortcut *shortcut = &shortcuts[i];
struct key_binding *binding = kywc_key_binding_create(shortcut->keybind, shortcut->desc);
if (!binding) {
continue;
}
if (!kywc_key_binding_register(binding, KEY_BINDING_TYPE_MAXIMIZED_VIEWS, shortcut_action,
shortcut)) {
kywc_key_binding_destroy(binding);
continue;
}
}
}
static void handle_theme_update(struct wl_listener *listener, void *data)
{
struct theme_update_event *update_event = data;
if (!(update_event->update_mask & THEME_UPDATE_MASK_TYPE)) {
return;
}
switcher->color = update_event->theme_type == THEME_TYPE_LIGHT ? &light : &dark;
ky_scene_rect_set_color(switcher->background, switcher->color->background_color);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&switcher->server_destroy.link);
wl_list_remove(&switcher->theme_update.link);
ky_scene_node_destroy(&switcher->tree->node);
free(switcher);
switcher = NULL;
}
bool maximize_switcher_create(struct view_manager *view_manager)
{
switcher = calloc(1, sizeof(*switcher));
if (!switcher) {
return false;
}
struct theme *theme = theme_manager_get_theme();
struct view_layer *layer = view_manager_get_layer(LAYER_ON_SCREEN_DISPLAY, false);
switcher->tree = ky_scene_tree_create(layer->tree);
ky_scene_node_set_enabled(&switcher->tree->node, false);
switch (theme->type) {
case THEME_TYPE_UNDEFINED:
case THEME_TYPE_LIGHT:
switcher->color = &light;
break;
case THEME_TYPE_DARK:
switcher->color = &dark;
break;
}
switcher->icon_opacity = ICON_OPACIPY;
switcher->background =
ky_scene_rect_create(switcher->tree, 0, 0, switcher->color->background_color);
switcher->pointer_grab.data = switcher;
switcher->pointer_grab.interface = &pointer_grab_impl;
switcher->keyboard_grab.data = switcher;
switcher->keyboard_grab.interface = &keyboard_grab_impl;
switcher->touch_grab.data = switcher;
switcher->touch_grab.interface = &touch_grab_impl;
switcher->theme_update.notify = handle_theme_update;
theme_manager_add_update_listener(&switcher->theme_update, false);
switcher->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(view_manager->server, &switcher->server_destroy);
switcher_register_shortcuts();
return true;
}
kylin-wayland-compositor/src/view/xdg_dialog.c 0000664 0001750 0001750 00000014702 15160461067 020477 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include "view_p.h"
#include "xdg-dialog-v1-protocol.h"
#define XDG_DIALOG_VERSION 1
struct xdg_dialog_manager {
struct wl_global *global;
struct wl_list xdg_dialogs;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct xdg_dialog {
struct wl_list link;
bool modal;
struct wlr_xdg_toplevel *toplevel;
struct wl_listener toplevel_destroy;
};
static struct xdg_dialog *xdg_dialog_from_toplevel(struct xdg_dialog_manager *manager,
struct wlr_xdg_toplevel *toplevel)
{
struct xdg_dialog *xdg_dialog;
wl_list_for_each(xdg_dialog, &manager->xdg_dialogs, link) {
if (xdg_dialog->toplevel == toplevel) {
return xdg_dialog;
}
}
return NULL;
}
static void handle_dialog_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void xdg_dialog_set_modal(struct xdg_dialog *xdg_dialog, bool modal)
{
xdg_dialog->modal = modal;
/* inert */
if (!xdg_dialog->toplevel) {
return;
}
struct view *view = view_try_from_wlr_surface(xdg_dialog->toplevel->base->surface);
kywc_view_set_modal(&view->base, xdg_dialog->modal);
}
static void handle_set_modal(struct wl_client *client, struct wl_resource *resource)
{
struct xdg_dialog *xdg_dialog = wl_resource_get_user_data(resource);
xdg_dialog_set_modal(xdg_dialog, true);
}
static void handle_unset_modal(struct wl_client *client, struct wl_resource *resource)
{
struct xdg_dialog *xdg_dialog = wl_resource_get_user_data(resource);
xdg_dialog_set_modal(xdg_dialog, false);
}
static const struct xdg_dialog_v1_interface xdg_dialog_v1_interface_impl = {
.destroy = handle_dialog_destroy,
.set_modal = handle_set_modal,
.unset_modal = handle_unset_modal,
};
static void xdg_dialog_handle_resource_destroy(struct wl_resource *resource)
{
struct xdg_dialog *xdg_dialog = wl_resource_get_user_data(resource);
wl_list_remove(&xdg_dialog->link);
wl_list_remove(&xdg_dialog->toplevel_destroy.link);
/* unapply its effects if xdg_toplevel is not destroyed */
xdg_dialog_set_modal(xdg_dialog, false);
free(xdg_dialog);
}
static void handle_toplevel_destroy(struct wl_listener *listener, void *data)
{
struct xdg_dialog *xdg_dialog = wl_container_of(listener, xdg_dialog, toplevel_destroy);
wl_list_remove(&xdg_dialog->toplevel_destroy.link);
wl_list_init(&xdg_dialog->toplevel_destroy.link);
/* If the xdg_toplevel is destroyed, the xdg_dialog_v1 becomes inert */
xdg_dialog->toplevel = NULL;
}
static void handle_get_xdg_dialog(struct wl_client *client, struct wl_resource *resource,
uint32_t id, struct wl_resource *toplevel)
{
struct xdg_dialog_manager *manager = wl_resource_get_user_data(resource);
struct wlr_xdg_toplevel *xdg_toplevel = wlr_xdg_toplevel_from_resource(toplevel);
/* the xdg_toplevel object has already been used to create a xdg_dialog_v1 */
struct xdg_dialog *xdg_dialog = NULL;
if (xdg_toplevel && (xdg_dialog = xdg_dialog_from_toplevel(manager, xdg_toplevel))) {
wl_resource_post_error(
resource, XDG_WM_DIALOG_V1_ERROR_ALREADY_USED,
"the xdg_toplevel object has already been used to create a xdg_dialog_v1");
return;
}
xdg_dialog = calloc(1, sizeof(*xdg_dialog));
if (!xdg_dialog) {
wl_client_post_no_memory(client);
return;
}
struct wl_resource *xdg_dialog_resource =
wl_resource_create(client, &xdg_dialog_v1_interface, wl_resource_get_version(resource), id);
if (!xdg_dialog_resource) {
free(xdg_dialog);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(xdg_dialog_resource, &xdg_dialog_v1_interface_impl, xdg_dialog,
xdg_dialog_handle_resource_destroy);
xdg_dialog->toplevel = xdg_toplevel;
wl_list_insert(&manager->xdg_dialogs, &xdg_dialog->link);
xdg_dialog->toplevel_destroy.notify = handle_toplevel_destroy;
wl_list_init(&xdg_dialog->toplevel_destroy.link);
if (!xdg_toplevel) {
return;
}
/* If this object is destroyed before the related xdg_toplevel */
wl_signal_add(&xdg_toplevel->events.destroy, &xdg_dialog->toplevel_destroy);
}
static void handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct xdg_wm_dialog_v1_interface xdg_wm_dialog_v1_interface_impl = {
.destroy = handle_destroy,
.get_xdg_dialog = handle_get_xdg_dialog,
};
static void xdg_dialog_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &xdg_wm_dialog_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
struct xdg_dialog_manager *manager = data;
wl_resource_set_implementation(resource, &xdg_wm_dialog_v1_interface_impl, manager, NULL);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
struct xdg_dialog_manager *manager = wl_container_of(listener, manager, display_destroy);
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct xdg_dialog_manager *manager = wl_container_of(listener, manager, server_destroy);
wl_list_remove(&manager->server_destroy.link);
free(manager);
}
bool xdg_dialog_create(struct server *server)
{
struct xdg_dialog_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->global = wl_global_create(server->display, &xdg_wm_dialog_v1_interface,
XDG_DIALOG_VERSION, manager, xdg_dialog_bind);
if (!manager->global) {
kywc_log(KYWC_WARN, "XDG dialog create failed");
free(manager);
return false;
}
wl_list_init(&manager->xdg_dialogs);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &manager->server_destroy);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &manager->display_destroy);
return true;
}
kylin-wayland-compositor/src/view/ukui_window.c 0000664 0001750 0001750 00000112071 15160461067 020740 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include
#include
#include
#include
#include "theme.h"
#include "ukui-window-management-protocol.h"
#include "view/workspace.h"
#include "view_p.h"
#define UKUI_WINDOW_MANAGEMENT_VERSION 1
struct ukui_window_management {
struct wl_global *global;
struct wl_list resources;
struct wl_list windows;
struct ukui_window *highlight_window;
struct wl_listener new_mapped_view;
struct wl_listener show_desktop;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct ukui_window {
struct wl_list resources;
struct wl_list link;
struct ukui_window_management *management;
bool minimizable, maximizable, fullscreenable;
bool closeable, movable, resizable;
bool focusable;
struct kywc_view *kywc_view;
struct wl_listener view_unmap;
struct wl_listener view_title;
struct wl_listener view_app_id;
struct wl_listener view_activate;
struct wl_listener view_minimize;
struct wl_listener view_maximize;
struct wl_listener view_fullscreen;
struct wl_listener view_above;
struct wl_listener view_below;
struct wl_listener view_skip_taskbar;
struct wl_listener view_skip_switcher;
struct wl_listener view_demands_attention;
struct wl_listener view_modal;
struct wl_listener view_capabilities;
struct wl_listener workspace_enter;
struct wl_listener workspace_leave;
struct wl_listener view_position;
struct wl_listener view_size;
struct wl_listener view_icon_update;
struct wl_listener view_update_capabilities;
struct wl_listener panel_surface_destroy;
/* The internal window id and uuid */
const char *uuid;
/* bitfield of state flags */
uint32_t states;
};
enum state_flag {
STATE_FLAG_ACTIVE = 0,
STATE_FLAG_MINIMIZED,
STATE_FLAG_MAXIMIZED,
STATE_FLAG_FULLSCREEN,
STATE_FLAG_KEEP_ABOVE,
STATE_FLAG_KEEP_BELOW,
STATE_FLAG_ON_ALL_DESKTOPS,
STATE_FLAG_DEMANDS_ATTENTION,
STATE_FLAG_CLOSEABLE,
STATE_FLAG_MINIMIZABLE,
STATE_FLAG_MAXIMIZABLE,
STATE_FLAG_FULLSCREENABLE,
STATE_FLAG_SHADEABLE,
STATE_FLAG_SHADED,
STATE_FLAG_MOVABLE,
STATE_FLAG_RESIZABLE,
STATE_FLAG_VIRTUAL_DESKTOP_CHANGEABLE,
STATE_FLAG_ACCEPT_FOCUS,
STATE_FLAG_SKIPTASKBAR,
STATE_FLAG_SKIPSWITCHER,
STATE_FLAG_MODALITY,
STATE_FLAG_LAST,
};
static void ukui_window_set_state(struct ukui_window *window, enum state_flag flag, bool state)
{
uint32_t mask = 0;
switch (flag) {
case STATE_FLAG_ACTIVE:
assert(state);
kywc_view_activate(window->kywc_view);
view_set_focus(view_from_kywc_view(window->kywc_view), input_manager_get_default_seat());
break;
case STATE_FLAG_MINIMIZED:
kywc_view_set_minimized(window->kywc_view, state);
break;
case STATE_FLAG_MAXIMIZED:
kywc_view_set_maximized(window->kywc_view, state, NULL);
break;
case STATE_FLAG_FULLSCREEN:
kywc_view_set_fullscreen(window->kywc_view, state, NULL);
break;
case STATE_FLAG_KEEP_ABOVE:
kywc_view_set_kept_above(window->kywc_view, state);
break;
case STATE_FLAG_KEEP_BELOW:
kywc_view_set_kept_below(window->kywc_view, state);
break;
case STATE_FLAG_ON_ALL_DESKTOPS:
break;
case STATE_FLAG_DEMANDS_ATTENTION:
window->kywc_view->demands_attention = state;
break;
case STATE_FLAG_CLOSEABLE:
window->closeable = state;
mask |= KYWC_VIEW_CLOSEABLE;
break;
case STATE_FLAG_MINIMIZABLE:
window->minimizable = state;
mask |= KYWC_VIEW_MINIMIZABLE | KYWC_VIEW_MINIMIZE_BUTTON;
break;
case STATE_FLAG_MAXIMIZABLE:
window->maximizable = state;
mask |= KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_MAXIMIZE_BUTTON;
break;
case STATE_FLAG_FULLSCREENABLE:
window->fullscreenable = state;
mask |= KYWC_VIEW_FULLSCREENABLE;
break;
case STATE_FLAG_SHADEABLE:
break;
case STATE_FLAG_SHADED:
break;
case STATE_FLAG_MOVABLE:
window->movable = state;
mask |= KYWC_VIEW_MOVABLE;
break;
case STATE_FLAG_RESIZABLE:
window->resizable = state;
mask |= KYWC_VIEW_RESIZABLE;
break;
case STATE_FLAG_VIRTUAL_DESKTOP_CHANGEABLE:
break;
case STATE_FLAG_ACCEPT_FOCUS:
window->focusable = state;
mask |= KYWC_VIEW_FOCUSABLE;
break;
case STATE_FLAG_SKIPTASKBAR:
window->kywc_view->skip_taskbar = state;
break;
case STATE_FLAG_SKIPSWITCHER:
window->kywc_view->skip_switcher = state;
break;
case STATE_FLAG_MODALITY:
kywc_view_set_modal(window->kywc_view, state);
break;
case STATE_FLAG_LAST:
break;
}
if (mask != 0) {
view_update_capabilities(view_from_kywc_view(window->kywc_view), mask);
}
}
static void ukui_window_send_state(struct ukui_window *window, struct wl_resource *resource,
bool force);
static void handle_set_state(struct wl_client *client, struct wl_resource *resource, uint32_t flags,
uint32_t state)
{
struct ukui_window *window = wl_resource_get_user_data(resource);
if (!window) {
return;
}
for (int i = 0; i < STATE_FLAG_LAST; i++) {
if ((flags >> i) & 0x1) {
ukui_window_set_state(window, i, (state >> i) & 0x1);
}
}
ukui_window_send_state(window, NULL, false);
}
static void handle_set_startup_geometry(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *entry, uint32_t x, uint32_t y,
uint32_t width, uint32_t height)
{
}
static void handle_panel_surface_destroy(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, panel_surface_destroy);
wl_list_remove(&window->panel_surface_destroy.link);
wl_list_init(&window->panel_surface_destroy.link);
struct view *view = view_from_kywc_view(window->kywc_view);
view->minimized_geometry.panel_surface = NULL;
}
static void handle_set_minimized_geometry(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *panel, uint32_t x, uint32_t y,
uint32_t width, uint32_t height)
{
struct ukui_window *window = wl_resource_get_user_data(resource);
if (!window) {
return;
}
struct wlr_surface *panel_surface = wlr_surface_from_resource(panel);
struct view *view = view_from_kywc_view(window->kywc_view);
view->minimized_geometry.panel_surface = panel_surface;
view->minimized_geometry.geometry = (struct kywc_box){ x, y, width, height };
wl_list_remove(&window->panel_surface_destroy.link);
wl_signal_add(&panel_surface->events.destroy, &window->panel_surface_destroy);
}
static void handle_unset_minimized_geometry(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *panel)
{
struct ukui_window *window = wl_resource_get_user_data(resource);
if (!window) {
return;
}
struct wlr_surface *panel_surface = wlr_surface_from_resource(panel);
struct view *view = view_from_kywc_view(window->kywc_view);
if (view->minimized_geometry.panel_surface &&
view->minimized_geometry.panel_surface == panel_surface) {
wl_list_remove(&window->panel_surface_destroy.link);
wl_list_init(&window->panel_surface_destroy.link);
view->minimized_geometry.panel_surface = NULL;
}
}
static void handle_close(struct wl_client *client, struct wl_resource *resource)
{
struct ukui_window *window = wl_resource_get_user_data(resource);
if (!window) {
return;
}
kywc_view_close(window->kywc_view);
}
static void handle_request_move(struct wl_client *client, struct wl_resource *resource)
{
// Not implemented yet
}
static void handle_request_resize(struct wl_client *client, struct wl_resource *resource)
{
// Not implemented yet
}
static void handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void handle_get_icon(struct wl_client *client, struct wl_resource *resource, int32_t fd)
{
struct ukui_window *window = wl_resource_get_user_data(resource);
if (!window) {
return;
}
struct view *view = view_from_kywc_view(window->kywc_view);
if (!view->impl->get_icon_buffer) {
return;
}
struct theme *theme = theme_manager_get_theme();
float scale = view->output->state.scale;
struct wlr_buffer *wlr_buffer = view->impl->get_icon_buffer(view, theme->icon_size, scale);
if (!wlr_buffer) {
return;
}
void *data;
uint32_t format;
size_t stride;
if (wlr_buffer->impl->begin_data_ptr_access &&
wlr_buffer->impl->begin_data_ptr_access(wlr_buffer, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data,
&format, &stride)) {
int32_t size[2];
size[0] = wlr_buffer->width;
size[1] = wlr_buffer->height;
write(fd, size, sizeof(size));
write(fd, data, stride * wlr_buffer->height);
close(fd);
wlr_buffer->impl->end_data_ptr_access(wlr_buffer);
}
}
static void handle_request_enter_virtual_desktop(struct wl_client *client,
struct wl_resource *resource, const char *id)
{
// Not implemented yet
}
static void handle_request_enter_new_virtual_desktop(struct wl_client *client,
struct wl_resource *resource)
{
// Not implemented yet
}
static void handle_request_leave_virtual_desktop(struct wl_client *client,
struct wl_resource *resource, const char *id)
{
// Not implemented yet
}
static void handle_request_enter_activity(struct wl_client *client, struct wl_resource *resource,
const char *id)
{
// Not implemented yet
}
static void handle_request_leave_activity(struct wl_client *client, struct wl_resource *resource,
const char *id)
{
// Not implemented yet
}
static void handle_send_to_output(struct wl_client *client, struct wl_resource *resource,
struct wl_resource *output)
{
// Not implemented yet
}
static void ukui_window_highlight(struct ukui_window *ukui_window, bool enable)
{
struct view *view = ukui_window ? view_from_kywc_view(ukui_window->kywc_view) : NULL;
highlight_view(view, enable);
}
static void handle_request_highlight(struct wl_client *client, struct wl_resource *resource)
{
struct ukui_window *window = wl_resource_get_user_data(resource);
if (!window || window->management->highlight_window == window) {
return;
}
if (window->management->highlight_window) {
ukui_window_highlight(window->management->highlight_window, false);
}
ukui_window_highlight(window, true);
window->management->highlight_window = window;
}
static void handle_request_unset_highlight(struct wl_client *client, struct wl_resource *resource)
{
struct ukui_window *window = wl_resource_get_user_data(resource);
if (!window || window->management->highlight_window != window) {
return;
}
ukui_window_highlight(window, false);
window->management->highlight_window = NULL;
}
static const struct ukui_window_interface ukui_window_impl = {
.set_state = handle_set_state,
.set_startup_geometry = handle_set_startup_geometry,
.set_minimized_geometry = handle_set_minimized_geometry,
.unset_minimized_geometry = handle_unset_minimized_geometry,
.close = handle_close,
.request_move = handle_request_move,
.request_resize = handle_request_resize,
.destroy = handle_destroy,
.get_icon = handle_get_icon,
.request_enter_virtual_desktop = handle_request_enter_virtual_desktop,
.request_enter_new_virtual_desktop = handle_request_enter_new_virtual_desktop,
.request_leave_virtual_desktop = handle_request_leave_virtual_desktop,
.request_enter_activity = handle_request_enter_activity,
.request_leave_activity = handle_request_leave_activity,
.send_to_output = handle_send_to_output,
.highlight = handle_request_highlight,
.unset_highlight = handle_request_unset_highlight,
};
static struct ukui_window *ukui_window_from_uuid(struct ukui_window_management *management,
const char *uuid)
{
struct ukui_window *window;
wl_list_for_each(window, &management->windows, link) {
if (strcmp(window->uuid, uuid) == 0) {
return window;
}
}
return NULL;
}
static void window_handle_resource_destroy(struct wl_resource *resource)
{
wl_resource_set_destructor(resource, NULL);
wl_resource_set_user_data(resource, NULL);
wl_list_remove(wl_resource_get_link(resource));
}
static void window_handle_view_title(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_title);
if (!window->kywc_view->title) {
return;
}
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_title_changed(resource, window->kywc_view->title);
}
}
static void window_handle_view_app_id(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_app_id);
if (!window->kywc_view->app_id) {
return;
}
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_app_id_changed(resource, window->kywc_view->app_id);
}
}
#define set_state(states, prop, state) \
if (prop) { \
states |= UKUI_WINDOW_STATE_##state; \
} else { \
states &= ~UKUI_WINDOW_STATE_##state; \
}
static void window_handle_view_activate(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_activate);
set_state(window->states, window->kywc_view->activated, ACTIVE);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_minimize(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_minimize);
set_state(window->states, window->kywc_view->minimized, MINIMIZED);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_maximize(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_maximize);
set_state(window->states, window->kywc_view->maximized, MAXIMIZED);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_fullscreen(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_fullscreen);
set_state(window->states, window->kywc_view->fullscreen, FULLSCREEN);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_above(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_above);
set_state(window->states, window->kywc_view->kept_above, KEEP_ABOVE);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_below(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_below);
set_state(window->states, window->kywc_view->kept_below, KEEP_BELOW);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_skip_taskbar(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_skip_taskbar);
set_state(window->states, window->kywc_view->skip_taskbar, SKIPTASKBAR);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_skip_switcher(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_skip_switcher);
set_state(window->states, window->kywc_view->skip_switcher, SKIPSWITCHER);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_demands_attention(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_demands_attention);
set_state(window->states, window->kywc_view->demands_attention, DEMANDS_ATTENTION);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_modal(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_modal);
set_state(window->states, window->kywc_view->modal, MODALITY);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
static void window_handle_view_capabilities(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_capabilities);
struct kywc_view_capabilities_event *event = data;
if (event->mask & (KYWC_VIEW_MINIMIZABLE | KYWC_VIEW_MAXIMIZABLE | KYWC_VIEW_CLOSEABLE |
KYWC_VIEW_FULLSCREENABLE | KYWC_VIEW_MOVABLE | KYWC_VIEW_RESIZABLE |
KYWC_VIEW_FOCUSABLE)) {
ukui_window_send_state(window, NULL, false);
}
}
static void window_handle_view_position(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_position);
struct kywc_view *view = window->kywc_view;
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_geometry(resource, view->geometry.x, view->geometry.y,
view->geometry.width, view->geometry.height);
}
}
static void window_handle_view_size(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_size);
struct kywc_view *view = window->kywc_view;
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_geometry(resource, view->geometry.x, view->geometry.y,
view->geometry.width, view->geometry.height);
}
}
static void ukui_window_send_state(struct ukui_window *window, struct wl_resource *resource,
bool force)
{
struct kywc_view *kywc_view = window->kywc_view;
uint32_t states = window->states;
set_state(states, kywc_view->activated, ACTIVE);
set_state(states, kywc_view->minimized, MINIMIZED);
set_state(states, kywc_view->maximized, MAXIMIZED);
set_state(states, kywc_view->fullscreen, FULLSCREEN);
set_state(states, kywc_view->kept_above, KEEP_ABOVE);
set_state(states, kywc_view->kept_below, KEEP_BELOW);
// ukui_window_MANAGEMENT_STATE_ON_ALL_DESKTOPS
set_state(states, kywc_view->demands_attention, DEMANDS_ATTENTION);
set_state(states, kywc_view->modal, MODALITY);
set_state(states, KYWC_VIEW_IS_CLOSEABLE(kywc_view), CLOSEABLE);
set_state(states, KYWC_VIEW_IS_MINIMIZABLE(kywc_view), MINIMIZABLE);
set_state(states, KYWC_VIEW_IS_MAXIMIZABLE(kywc_view), MAXIMIZABLE);
set_state(states, KYWC_VIEW_IS_FULLSCREENABLE(kywc_view), FULLSCREENABLE);
set_state(states, kywc_view->skip_taskbar, SKIPTASKBAR);
// ukui_window_MANAGEMENT_STATE_SHADEABLE
// ukui_window_MANAGEMENT_STATE_SHADED
set_state(states, KYWC_VIEW_IS_MOVABLE(kywc_view), MOVABLE);
set_state(states, KYWC_VIEW_IS_RESIZABLE(kywc_view), RESIZABLE);
// ukui_window_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE
set_state(states, kywc_view->skip_switcher, SKIPSWITCHER);
set_state(states, KYWC_VIEW_IS_FOCUSABLE(kywc_view), ACCEPT_FOCUS);
if (force || states != window->states) {
window->states = states;
if (resource) {
ukui_window_send_state_changed(resource, window->states);
} else {
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_state_changed(resource, window->states);
}
}
}
}
#undef set_state
static void ukui_window_add_resource(struct ukui_window *window,
struct wl_resource *management_resource, uint32_t id)
{
struct wl_client *client = wl_resource_get_client(management_resource);
uint32_t version = wl_resource_get_version(management_resource);
struct wl_resource *resource = wl_resource_create(client, &ukui_window_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &ukui_window_impl, window,
window_handle_resource_destroy);
if (!window) {
wl_list_init(wl_resource_get_link(resource));
ukui_window_send_unmapped(resource);
return;
}
wl_list_insert(&window->resources, wl_resource_get_link(resource));
/* send states */
ukui_window_send_state(window, resource, true);
struct kywc_view *kywc_view = window->kywc_view;
struct view *view = view_from_kywc_view(kywc_view);
if (kywc_view->title) {
ukui_window_send_title_changed(resource, kywc_view->title);
}
if (kywc_view->app_id) {
ukui_window_send_app_id_changed(resource, kywc_view->app_id);
}
if (view->pid) {
ukui_window_send_pid_changed(resource, view->pid);
}
ukui_window_send_geometry(resource, kywc_view->geometry.x, kywc_view->geometry.y,
kywc_view->geometry.width, kywc_view->geometry.height);
// ukui_window_send_parent_window
// ukui_window_send_virtual_desktop_changed
// ukui_window_send_virtual_desktop_entered
// ukui_window_send_virtual_desktop_left
if (!theme_icon_is_fallback(view->icon)) {
const char *icon_name = theme_icon_get_name(view->icon);
ukui_window_send_themed_icon_name_changed(resource, icon_name);
} else {
ukui_window_send_icon_changed(resource);
}
// ukui_window_send_application_menu
// ukui_window_send_activity_entered
// ukui_window_send_activity_left
// ukui_window_send_resource_name_changed
ukui_window_send_initial_state(resource);
struct view_proxy *proxy;
wl_list_for_each(proxy, &view->view_proxies, view_link) {
ukui_window_send_virtual_desktop_entered(resource, proxy->workspace->uuid);
}
}
static void handle_create_window(struct wl_client *client, struct wl_resource *management_resource,
uint32_t id, const char *internal_window_uuid)
{
struct ukui_window_management *management = wl_resource_get_user_data(management_resource);
struct ukui_window *window = ukui_window_from_uuid(management, internal_window_uuid);
ukui_window_add_resource(window, management_resource, id);
}
static void handle_show_desktop(struct wl_client *client, struct wl_resource *resource,
uint32_t state)
{
view_manager_show_desktop(state == UKUI_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED, true);
}
static const struct ukui_window_management_interface ukui_window_management_impl = {
.show_desktop = handle_show_desktop,
.create_window = handle_create_window,
};
static void management_handle_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
}
static void ukui_window_management_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct ukui_window_management *management = data;
struct wl_resource *resource =
wl_resource_create(client, &ukui_window_management_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_list_insert(&management->resources, wl_resource_get_link(resource));
wl_resource_set_implementation(resource, &ukui_window_management_impl, management,
management_handle_resource_destroy);
ukui_window_management_send_show_desktop_changed(
resource, view_manager_get_show_desktop() ? UKUI_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED
: UKUI_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED);
struct ukui_window *window;
wl_list_for_each(window, &management->windows, link) {
ukui_window_management_send_window_created(resource, window->uuid);
}
// TODO: stacking_order_changed
}
static void handle_window_workspace_enter(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, workspace_enter);
struct workspace *workspace = data;
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_virtual_desktop_entered(resource, workspace->uuid);
}
}
static void handle_window_workspace_leave(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, workspace_leave);
struct workspace *workspace = data;
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_virtual_desktop_left(resource, workspace->uuid);
}
}
static void window_handle_view_unmap(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_unmap);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
ukui_window_send_unmapped(resource);
}
wl_list_remove(&window->view_unmap.link);
wl_list_remove(&window->view_title.link);
wl_list_remove(&window->view_app_id.link);
wl_list_remove(&window->view_activate.link);
wl_list_remove(&window->view_minimize.link);
wl_list_remove(&window->view_maximize.link);
wl_list_remove(&window->view_fullscreen.link);
wl_list_remove(&window->view_above.link);
wl_list_remove(&window->view_below.link);
wl_list_remove(&window->view_skip_taskbar.link);
wl_list_remove(&window->view_skip_switcher.link);
wl_list_remove(&window->view_demands_attention.link);
wl_list_remove(&window->view_modal.link);
wl_list_remove(&window->view_capabilities.link);
wl_list_remove(&window->workspace_enter.link);
wl_list_remove(&window->workspace_leave.link);
wl_list_remove(&window->view_position.link);
wl_list_remove(&window->view_size.link);
wl_list_remove(&window->view_icon_update.link);
wl_list_remove(&window->view_update_capabilities.link);
wl_list_remove(&window->panel_surface_destroy.link);
wl_list_remove(&window->link);
struct wl_resource *tmp;
wl_resource_for_each_safe(resource, tmp, &window->resources) {
window_handle_resource_destroy(resource);
}
if (window->management->highlight_window == window) {
window->management->highlight_window = NULL;
ukui_window_highlight(window->management->highlight_window, false);
}
free(window);
}
static void window_handle_view_icon_update(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_icon_update);
struct view *view = view_from_kywc_view(window->kywc_view);
struct wl_resource *resource;
wl_resource_for_each(resource, &window->resources) {
if (!theme_icon_is_fallback(view->icon)) {
const char *icon_name = theme_icon_get_name(view->icon);
ukui_window_send_themed_icon_name_changed(resource, icon_name);
} else {
ukui_window_send_icon_changed(resource);
}
}
}
static void window_handle_view_update_capabilities(struct wl_listener *listener, void *data)
{
struct ukui_window *window = wl_container_of(listener, window, view_update_capabilities);
struct view_update_capabilities_event *event = data;
if (event->mask & KYWC_VIEW_MINIMIZABLE) {
if (!window->minimizable) {
event->state &= ~KYWC_VIEW_MINIMIZABLE;
}
}
if (event->mask & KYWC_VIEW_MAXIMIZABLE) {
if (!window->maximizable) {
event->state &= ~KYWC_VIEW_MAXIMIZABLE;
}
}
if (event->mask & KYWC_VIEW_CLOSEABLE) {
if (!window->closeable) {
event->state &= ~KYWC_VIEW_CLOSEABLE;
}
}
if (event->mask & KYWC_VIEW_FULLSCREENABLE) {
if (!window->fullscreenable) {
event->state &= ~KYWC_VIEW_FULLSCREENABLE;
}
}
if (event->mask & KYWC_VIEW_MOVABLE) {
if (!window->movable) {
event->state &= ~KYWC_VIEW_MOVABLE;
}
}
if (event->mask & KYWC_VIEW_RESIZABLE) {
if (!window->resizable) {
event->state &= ~KYWC_VIEW_RESIZABLE;
}
}
if (event->mask & KYWC_VIEW_FOCUSABLE) {
if (!window->focusable) {
event->state &= ~KYWC_VIEW_FOCUSABLE;
}
}
if (event->mask & KYWC_VIEW_MINIMIZE_BUTTON) {
if (!window->minimizable) {
event->state &= ~KYWC_VIEW_MINIMIZE_BUTTON;
}
}
if (event->mask & KYWC_VIEW_MAXIMIZE_BUTTON) {
if (!window->maximizable) {
event->state &= ~KYWC_VIEW_MAXIMIZE_BUTTON;
}
}
}
static void handle_new_mapped_view(struct wl_listener *listener, void *data)
{
struct ukui_window_management *management =
wl_container_of(listener, management, new_mapped_view);
struct kywc_view *kywc_view = data;
struct view *view = view_from_kywc_view(kywc_view);
struct ukui_window *window = calloc(1, sizeof(*window));
if (!window) {
return;
}
window->management = management;
wl_list_init(&window->resources);
wl_list_insert(&management->windows, &window->link);
window->uuid = kywc_view->uuid;
window->minimizable = window->maximizable = window->fullscreenable = window->closeable =
window->movable = window->resizable = window->focusable = true;
window->kywc_view = kywc_view;
window->view_unmap.notify = window_handle_view_unmap;
wl_signal_add(&kywc_view->events.unmap, &window->view_unmap);
window->view_title.notify = window_handle_view_title;
wl_signal_add(&kywc_view->events.title, &window->view_title);
window->view_app_id.notify = window_handle_view_app_id;
wl_signal_add(&kywc_view->events.app_id, &window->view_app_id);
window->view_activate.notify = window_handle_view_activate;
wl_signal_add(&kywc_view->events.activate, &window->view_activate);
window->view_minimize.notify = window_handle_view_minimize;
wl_signal_add(&kywc_view->events.minimize, &window->view_minimize);
window->view_maximize.notify = window_handle_view_maximize;
wl_signal_add(&kywc_view->events.maximize, &window->view_maximize);
window->view_fullscreen.notify = window_handle_view_fullscreen;
wl_signal_add(&kywc_view->events.fullscreen, &window->view_fullscreen);
window->view_above.notify = window_handle_view_above;
wl_signal_add(&kywc_view->events.above, &window->view_above);
window->view_below.notify = window_handle_view_below;
wl_signal_add(&kywc_view->events.below, &window->view_below);
window->view_skip_taskbar.notify = window_handle_view_skip_taskbar;
wl_signal_add(&kywc_view->events.skip_taskbar, &window->view_skip_taskbar);
window->view_skip_switcher.notify = window_handle_view_skip_switcher;
wl_signal_add(&kywc_view->events.skip_switcher, &window->view_skip_switcher);
window->view_demands_attention.notify = window_handle_view_demands_attention;
wl_signal_add(&kywc_view->events.demands_attention, &window->view_demands_attention);
window->view_modal.notify = window_handle_view_modal;
wl_signal_add(&kywc_view->events.modal, &window->view_modal);
window->view_capabilities.notify = window_handle_view_capabilities;
wl_signal_add(&kywc_view->events.capabilities, &window->view_capabilities);
window->workspace_enter.notify = handle_window_workspace_enter;
wl_signal_add(&view->events.workspace_enter, &window->workspace_enter);
window->workspace_leave.notify = handle_window_workspace_leave;
wl_signal_add(&view->events.workspace_leave, &window->workspace_leave);
window->view_position.notify = window_handle_view_position;
wl_signal_add(&kywc_view->events.position, &window->view_position);
window->view_size.notify = window_handle_view_size;
wl_signal_add(&kywc_view->events.size, &window->view_size);
window->view_icon_update.notify = window_handle_view_icon_update;
wl_signal_add(&view->events.icon_update, &window->view_icon_update);
window->view_update_capabilities.notify = window_handle_view_update_capabilities;
view_add_update_capabilities_listener(view, &window->view_update_capabilities);
window->panel_surface_destroy.notify = handle_panel_surface_destroy;
wl_list_init(&window->panel_surface_destroy.link);
struct wl_resource *resource;
wl_resource_for_each(resource, &management->resources) {
ukui_window_management_send_window_created(resource, window->uuid);
}
if (management->highlight_window) {
ukui_window_highlight(management->highlight_window, false);
management->highlight_window = NULL;
}
}
static void handle_shown_desktop(struct wl_listener *listener, void *data)
{
struct ukui_window_management *management = wl_container_of(listener, management, show_desktop);
bool enabled = view_manager_get_show_desktop();
struct wl_resource *resource;
wl_resource_for_each(resource, &management->resources) {
ukui_window_management_send_show_desktop_changed(
resource, enabled ? UKUI_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED
: UKUI_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED);
}
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
struct ukui_window_management *management =
wl_container_of(listener, management, display_destroy);
wl_list_remove(&management->display_destroy.link);
wl_list_remove(&management->new_mapped_view.link);
wl_list_remove(&management->show_desktop.link);
wl_global_destroy(management->global);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
struct ukui_window_management *management =
wl_container_of(listener, management, server_destroy);
wl_list_remove(&management->server_destroy.link);
free(management);
}
bool ukui_window_management_create(struct server *server)
{
struct ukui_window_management *management = calloc(1, sizeof(*management));
if (!management) {
return false;
}
management->global =
wl_global_create(server->display, &ukui_window_management_interface,
UKUI_WINDOW_MANAGEMENT_VERSION, management, ukui_window_management_bind);
if (!management->global) {
kywc_log(KYWC_WARN, "UKUI window management create failed");
free(management);
return false;
}
wl_list_init(&management->windows);
wl_list_init(&management->resources);
management->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &management->server_destroy);
management->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &management->display_destroy);
management->new_mapped_view.notify = handle_new_mapped_view;
kywc_view_add_new_mapped_listener(&management->new_mapped_view);
management->show_desktop.notify = handle_shown_desktop;
view_manager_add_show_desktop_listener(&management->show_desktop);
return true;
}
kylin-wayland-compositor/src/view/xdg_activation.c 0000664 0001750 0001750 00000016075 15160461067 021406 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include
#include
#include "input/seat.h"
#include "output.h"
#include "view/workspace.h"
#include "view_p.h"
struct xdg_activation_token {
struct wlr_xdg_activation_token_v1 *token;
struct wl_listener token_destroy;
struct seat *seat;
struct wl_listener seat_destroy;
struct kywc_output *output;
struct wl_listener output_destroy;
struct workspace *workspace;
struct wl_listener workspace_destroy;
struct view *view;
struct wl_listener view_premap;
struct wl_listener view_destroy;
};
struct xdg_activation_manager {
struct wlr_xdg_activation_v1 *xdg_activation;
struct wl_listener new_token;
struct wl_listener request_activate;
struct wl_listener destroy;
};
static void xdg_activation_token_destroy(struct xdg_activation_token *token)
{
if (token->view) {
return;
}
wl_list_remove(&token->token_destroy.link);
wl_list_remove(&token->seat_destroy.link);
wl_list_remove(&token->output_destroy.link);
wl_list_remove(&token->workspace_destroy.link);
wl_list_remove(&token->view_premap.link);
wl_list_remove(&token->view_destroy.link);
free(token);
}
static void token_handle_seat_destroy(struct wl_listener *listener, void *data)
{
struct xdg_activation_token *token = wl_container_of(listener, token, seat_destroy);
token->seat = NULL;
wl_list_remove(&token->seat_destroy.link);
wl_list_init(&token->seat_destroy.link);
}
static void token_handle_output_destroy(struct wl_listener *listener, void *data)
{
struct xdg_activation_token *token = wl_container_of(listener, token, output_destroy);
token->output = NULL;
wl_list_remove(&token->output_destroy.link);
wl_list_init(&token->output_destroy.link);
}
static void token_handle_workspace_destroy(struct wl_listener *listener, void *data)
{
struct xdg_activation_token *token = wl_container_of(listener, token, workspace_destroy);
token->workspace = NULL;
wl_list_remove(&token->workspace_destroy.link);
wl_list_init(&token->workspace_destroy.link);
}
static void token_handle_token_destroy(struct wl_listener *listener, void *data)
{
struct xdg_activation_token *token = wl_container_of(listener, token, token_destroy);
token->token = NULL;
xdg_activation_token_destroy(token);
}
static void token_handle_view_premap(struct wl_listener *listener, void *data)
{
struct xdg_activation_token *token = wl_container_of(listener, token, view_premap);
struct view *view = token->view;
/* set view output */
if (token->output && view->output != token->output) {
view->output = token->output;
wl_list_remove(&view->output_destroy.link);
wl_signal_add(&view->output->events.destroy, &view->output_destroy);
}
if (token->workspace) {
view_set_workspace(view, token->workspace);
}
view->base.focused_seat = token->seat ? token->seat : input_manager_get_default_seat();
token->view = NULL;
xdg_activation_token_destroy(token);
}
static void token_handle_view_destroy(struct wl_listener *listener, void *data)
{
struct xdg_activation_token *token = wl_container_of(listener, token, view_destroy);
token->view = NULL;
xdg_activation_token_destroy(token);
}
static void handle_request_activate(struct wl_listener *listener, void *data)
{
const struct wlr_xdg_activation_v1_request_activate_event *event = data;
struct wlr_xdg_surface *wlr_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(event->surface);
struct xdg_activation_token *token = event->token->data;
if (!wlr_xdg_surface || !token) {
return;
}
struct view *view = view_try_from_wlr_surface(event->surface);
if (!view) {
return;
}
if (event->surface->mapped) {
/* activation request */
kywc_view_activate(&view->base);
view_set_focus(view, token->seat ? token->seat : input_manager_get_default_seat());
return;
}
/* startup app notification. now token destroy by view_premap or view_destroy*/
wl_list_remove(&token->token_destroy.link);
wl_list_init(&token->token_destroy.link);
token->view = view;
token->view_premap.notify = token_handle_view_premap;
wl_signal_add(&token->view->base.events.premap, &token->view_premap);
token->view_destroy.notify = token_handle_view_destroy;
wl_signal_add(&token->view->base.events.destroy, &token->view_destroy);
}
static void handle_new_token(struct wl_listener *listener, void *data)
{
struct xdg_activation_token *token = calloc(1, sizeof(*token));
if (!token) {
return;
}
struct wlr_xdg_activation_token_v1 *token_v1 = data;
token_v1->data = token;
token->token = token_v1;
token->token_destroy.notify = token_handle_token_destroy;
wl_signal_add(&token_v1->events.destroy, &token->token_destroy);
struct seat *seat =
token_v1->seat ? seat_from_wlr_seat(token_v1->seat) : input_manager_get_default_seat();
token->seat = seat;
token->seat_destroy.notify = token_handle_seat_destroy;
wl_signal_add(&seat->events.destroy, &token->seat_destroy);
token->output = input_current_output(seat) ? &input_current_output(seat)->base : NULL;
token->output_destroy.notify = token_handle_output_destroy;
wl_signal_add(&token->output->events.destroy, &token->output_destroy);
token->workspace = workspace_manager_get_current();
if (token_v1->surface) {
/* workspace from surface */
struct view *view = view_try_from_wlr_surface(token_v1->surface);
if (view) {
token->workspace = view->current_proxy->workspace;
}
}
token->workspace_destroy.notify = token_handle_workspace_destroy;
wl_signal_add(&token->workspace->events.destroy, &token->workspace_destroy);
wl_list_init(&token->view_premap.link);
wl_list_init(&token->view_destroy.link);
}
static void handle_destroy(struct wl_listener *listener, void *data)
{
struct xdg_activation_manager *manager = wl_container_of(listener, manager, destroy);
wl_list_remove(&manager->destroy.link);
wl_list_remove(&manager->request_activate.link);
wl_list_remove(&manager->new_token.link);
free(manager);
}
bool xdg_activation_create(struct server *server)
{
struct xdg_activation_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->xdg_activation = wlr_xdg_activation_v1_create(server->display);
if (!manager->xdg_activation) {
free(manager);
return false;
}
manager->request_activate.notify = handle_request_activate;
wl_signal_add(&manager->xdg_activation->events.request_activate, &manager->request_activate);
manager->new_token.notify = handle_new_token;
wl_signal_add(&manager->xdg_activation->events.new_token, &manager->new_token);
manager->destroy.notify = handle_destroy;
wl_signal_add(&manager->xdg_activation->events.destroy, &manager->destroy);
return true;
}
kylin-wayland-compositor/src/view/ukui_blur.c 0000664 0001750 0001750 00000025753 15160461067 020407 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include "scene/surface.h"
#include "ukui-blur-v1-protocol.h"
#include "util/macros.h"
#include "view_p.h"
#define UKUI_BLUR_MANAGER_VERSION 2
enum UKUI_BLUR_STATE_MASK {
UKUI_BLUR_STATE_NONE = 0,
UKUI_BLUR_STATE_REGION = 1 << 0,
UKUI_BLUR_STATE_LEVEL = 1 << 1,
UKUI_BLUR_STATE_OPACITY = 1 << 2,
};
struct ukui_blur_manager {
struct wl_global *global;
struct wl_list ukui_blur_surfaces;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct ukui_blur_surface {
struct wl_list link;
struct wl_resource *resource;
struct wlr_surface *wlr_surface;
struct wl_listener surface_commit;
struct wl_listener surface_map;
struct wl_listener surface_destroy;
struct ky_scene_buffer *scene_buffer;
struct wl_listener node_destroy;
pixman_region32_t pending_region;
uint32_t pending_level;
float pending_opacity;
uint32_t pending_mask;
};
static struct ukui_blur_manager *manager = NULL;
struct blur_level {
int iterations;
float offset;
};
static const struct blur_level blur_levels[] = {
{ 1, 1.5 }, { 1, 2 }, { 2, 2.5 }, { 2, 3.0 }, { 3, 2.6 },
{ 3, 3.2 }, { 3, 3.8 }, { 3, 4.4 }, { 3, 5 }, { 4, 3.83333 },
{ 4, 4.66667 }, { 4, 5.5 }, { 4, 6.33333 }, { 4, 7.16667 }, { 4, 8 },
};
static void blur_surface_apply_state(struct ukui_blur_surface *blur_surface)
{
/* the level is twelve, if not set. iterations = 4, offset = 5.5 */
if (blur_surface->pending_mask & UKUI_BLUR_STATE_REGION) {
ky_scene_node_set_blur_region(&blur_surface->scene_buffer->node,
&blur_surface->pending_region);
}
if (blur_surface->pending_mask & UKUI_BLUR_STATE_LEVEL) {
const struct blur_level *level = &blur_levels[blur_surface->pending_level - 1];
ky_scene_node_set_blur_level(&blur_surface->scene_buffer->node, level->iterations,
level->offset);
}
if (blur_surface->pending_mask & UKUI_BLUR_STATE_OPACITY) {
ky_scene_node_set_blur_opacity(&blur_surface->scene_buffer->node,
blur_surface->pending_opacity);
}
blur_surface->pending_mask = UKUI_BLUR_STATE_NONE;
}
static void blur_surface_destroy(struct ukui_blur_surface *blur_surface)
{
/* remove blur if surface is not destroyed */
if (blur_surface->scene_buffer) {
ky_scene_node_set_blur_region(&blur_surface->scene_buffer->node, NULL);
}
/* clear destructor when surface destroyed before blur resources */
wl_resource_set_user_data(blur_surface->resource, NULL);
wl_resource_set_destructor(blur_surface->resource, NULL);
wl_list_remove(&blur_surface->link);
wl_list_remove(&blur_surface->surface_commit.link);
wl_list_remove(&blur_surface->surface_map.link);
wl_list_remove(&blur_surface->surface_destroy.link);
wl_list_remove(&blur_surface->node_destroy.link);
pixman_region32_fini(&blur_surface->pending_region);
free(blur_surface);
}
static void blur_surface_handle_node_destroy(struct wl_listener *listener, void *data)
{
struct ukui_blur_surface *blur_surface = wl_container_of(listener, blur_surface, node_destroy);
blur_surface->scene_buffer = NULL;
blur_surface_destroy(blur_surface);
}
static void blur_surface_handle_surface_map(struct wl_listener *listener, void *data)
{
struct ukui_blur_surface *blur_surface = wl_container_of(listener, blur_surface, surface_map);
blur_surface->scene_buffer = ky_scene_buffer_try_from_surface(blur_surface->wlr_surface);
wl_signal_add(&blur_surface->scene_buffer->node.events.destroy, &blur_surface->node_destroy);
}
static void blur_surface_handle_surface_destroy(struct wl_listener *listener, void *data)
{
struct ukui_blur_surface *blur_surface =
wl_container_of(listener, blur_surface, surface_destroy);
blur_surface_destroy(blur_surface);
}
static void blur_surface_handle_surface_commit(struct wl_listener *listener, void *data)
{
struct ukui_blur_surface *blur_surface =
wl_container_of(listener, blur_surface, surface_commit);
if (!blur_surface->wlr_surface->mapped) {
return;
}
if (blur_surface->pending_mask != UKUI_BLUR_STATE_NONE) {
blur_surface_apply_state(blur_surface);
}
}
static void ukui_blur_surface_handle_resource_destroy(struct wl_resource *resource)
{
struct ukui_blur_surface *blur_surface = wl_resource_get_user_data(resource);
blur_surface_destroy(blur_surface);
}
static void ukui_blur_surface_handle_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static void ukui_blur_surface_handle_set_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource)
{
struct ukui_blur_surface *blur_surface = wl_resource_get_user_data(resource);
if (!blur_surface) {
return;
}
if (region_resource) {
const pixman_region32_t *region = wlr_region_from_resource(region_resource);
pixman_region32_copy(&blur_surface->pending_region, region);
} else {
pixman_region32_clear(&blur_surface->pending_region);
}
blur_surface->pending_mask |= UKUI_BLUR_STATE_REGION;
}
static void ukui_blur_surface_handle_set_level(struct wl_client *client,
struct wl_resource *resource, uint32_t level)
{
struct ukui_blur_surface *blur_surface = wl_resource_get_user_data(resource);
if (!blur_surface) {
return;
}
if (level < 1 || level > 15) {
kywc_log(KYWC_DEBUG, "UKUI blur level is invalid");
return;
}
blur_surface->pending_level = level;
blur_surface->pending_mask |= UKUI_BLUR_STATE_LEVEL;
}
static void ukui_blur_surface_handle_set_opacity(struct wl_client *client,
struct wl_resource *resource, wl_fixed_t opacity)
{
struct ukui_blur_surface *blur_surface = wl_resource_get_user_data(resource);
if (!blur_surface) {
return;
}
blur_surface->pending_opacity = CLAMP(wl_fixed_to_double(opacity), 0.0f, 1.0f);
blur_surface->pending_mask |= UKUI_BLUR_STATE_OPACITY;
}
static const struct ukui_blur_surface_v1_interface ukui_blur_surface_impl = {
.destroy = ukui_blur_surface_handle_destroy,
.set_region = ukui_blur_surface_handle_set_region,
.set_level = ukui_blur_surface_handle_set_level,
.set_opacity = ukui_blur_surface_handle_set_opacity,
};
static struct ukui_blur_surface *blur_surface_from_wlr_surface(struct wlr_surface *wlr_surface)
{
struct ukui_blur_surface *blur_surface, *tmp;
wl_list_for_each_safe(blur_surface, tmp, &manager->ukui_blur_surfaces, link) {
if (blur_surface->wlr_surface == wlr_surface) {
return blur_surface;
}
}
return NULL;
}
static void handle_blur_manager_get_blur(struct wl_client *client,
struct wl_resource *manager_resource, uint32_t id,
struct wl_resource *surface_resource)
{
struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource);
struct ukui_blur_surface *blur_surface = blur_surface_from_wlr_surface(wlr_surface);
if (blur_surface) {
wl_resource_post_error(manager_resource, UKUI_BLUR_MANAGER_V1_ERROR_BLUR_EXISTS,
"ukui blur surface already exists for this surface");
return;
}
blur_surface = calloc(1, sizeof(*blur_surface));
if (!blur_surface) {
wl_resource_post_no_memory(manager_resource);
return;
}
int version = wl_resource_get_version(manager_resource);
struct wl_resource *resource =
wl_resource_create(client, &ukui_blur_surface_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
free(blur_surface);
return;
}
blur_surface->resource = resource;
wl_list_insert(&manager->ukui_blur_surfaces, &blur_surface->link);
pixman_region32_init(&blur_surface->pending_region);
blur_surface->wlr_surface = wlr_surface;
blur_surface->surface_map.notify = blur_surface_handle_surface_map;
wl_signal_add(&wlr_surface->events.map, &blur_surface->surface_map);
blur_surface->surface_destroy.notify = blur_surface_handle_surface_destroy;
wl_signal_add(&wlr_surface->events.destroy, &blur_surface->surface_destroy);
blur_surface->surface_commit.notify = blur_surface_handle_surface_commit;
wl_signal_add(&wlr_surface->events.commit, &blur_surface->surface_commit);
blur_surface->node_destroy.notify = blur_surface_handle_node_destroy;
wl_list_init(&blur_surface->node_destroy.link);
wl_resource_set_implementation(resource, &ukui_blur_surface_impl, blur_surface,
ukui_blur_surface_handle_resource_destroy);
if (wlr_surface->mapped) {
blur_surface_handle_surface_map(&blur_surface->surface_map, NULL);
}
}
static void handle_blur_manager_destroy(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct ukui_blur_manager_v1_interface ukui_blur_manager_v1_impl = {
.destroy = handle_blur_manager_destroy,
.get_blur = handle_blur_manager_get_blur,
};
static void ukui_blur_manager_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &ukui_blur_manager_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &ukui_blur_manager_v1_impl, manager, NULL);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->server_destroy.link);
free(manager);
manager = NULL;
}
bool ukui_blur_manager_create(struct server *server)
{
manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->global = wl_global_create(server->display, &ukui_blur_manager_v1_interface,
UKUI_BLUR_MANAGER_VERSION, manager, ukui_blur_manager_bind);
if (!manager->global) {
kywc_log(KYWC_WARN, "UKUI blur manager create failed");
free(manager);
return false;
}
wl_list_init(&manager->ukui_blur_surfaces);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &manager->server_destroy);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &manager->display_destroy);
return true;
}
kylin-wayland-compositor/src/view/kde_slide.c 0000664 0001750 0001750 00000020102 15160461067 020310 0 ustar feng feng // SPDX-FileCopyrightText: 2024 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include
#include
#include "slide-protocol.h"
#include "util/wayland.h"
#include "view_p.h"
#define KDE_KWIN_SLIDE_MANAGER_VERSION 1
struct kde_slide_manager {
struct wl_global *global;
struct wl_list kde_slides;
struct wl_listener display_destroy;
struct wl_listener server_destroy;
};
struct kde_slide {
struct wl_list link;
struct wl_list resources;
struct wlr_surface *wlr_surface;
struct wl_listener surface_map;
struct wl_listener surface_destroy;
uint32_t location, pending_location;
int32_t offset, pending_offset;
};
static struct kde_slide_manager *manager = NULL;
static void kde_slide_apply_state(struct kde_slide *slide)
{
slide->offset = slide->pending_offset;
slide->location = slide->pending_location;
}
static struct kde_slide *kde_slide_from_wlr_surface(struct wlr_surface *wlr_surface)
{
struct kde_slide *slide;
wl_list_for_each(slide, &manager->kde_slides, link) {
if (slide->wlr_surface == wlr_surface) {
return slide;
}
}
return NULL;
}
static void view_apply_slide(struct kde_slide *slide)
{
struct view *view = view_try_from_wlr_surface(slide->wlr_surface);
if (!view) {
kywc_log(KYWC_DEBUG, "Surface is not a toplevel");
return;
}
view->slide.offset = slide->offset;
view->slide.location = slide->location;
view->use_slide = true;
}
static void kde_slide_handle_commit(struct wl_client *client, struct wl_resource *resource)
{
struct kde_slide *slide = wl_resource_get_user_data(resource);
if (!slide) {
return;
}
kde_slide_apply_state(slide);
if (slide->wlr_surface->mapped) {
view_apply_slide(slide);
}
}
static void kde_slide_handle_set_location(struct wl_client *client, struct wl_resource *resource,
uint32_t location)
{
struct kde_slide *slide = wl_resource_get_user_data(resource);
if (!slide) {
return;
}
if (location != slide->location) {
slide->pending_location = location;
}
}
static void kde_slide_handle_set_offset(struct wl_client *client, struct wl_resource *resource,
int32_t offset)
{
struct kde_slide *slide = wl_resource_get_user_data(resource);
if (!slide) {
return;
}
if (offset != slide->pending_offset) {
slide->pending_offset = offset;
}
}
static void kde_slide_handle_release(struct wl_client *client, struct wl_resource *resource)
{
wl_resource_destroy(resource);
}
static const struct org_kde_kwin_slide_interface kde_slide_impl = {
.commit = kde_slide_handle_commit,
.set_location = kde_slide_handle_set_location,
.set_offset = kde_slide_handle_set_offset,
.release = kde_slide_handle_release,
};
static void kde_slide_destroy(struct kde_slide *slide)
{
/* clear destructor when surface destroyed before slide resources */
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &slide->resources) {
wl_resource_set_user_data(resource, NULL);
wl_resource_set_destructor(resource, NULL);
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
}
wl_list_remove(&slide->link);
wl_list_remove(&slide->surface_map.link);
wl_list_remove(&slide->surface_destroy.link);
free(slide);
}
static void kde_slide_handle_resource_destroy(struct wl_resource *resource)
{
wl_list_remove(wl_resource_get_link(resource));
/* destroy slide if no slide resource */
struct kde_slide *slide = wl_resource_get_user_data(resource);
if (slide && wl_list_empty(&slide->resources)) {
kde_slide_destroy(slide);
}
}
static void slide_handle_surface_destroy(struct wl_listener *listener, void *data)
{
struct kde_slide *slide = wl_container_of(listener, slide, surface_destroy);
slide->wlr_surface = NULL;
kde_slide_destroy(slide);
}
static void slide_handle_surface_map(struct wl_listener *listener, void *data)
{
struct kde_slide *slide = wl_container_of(listener, slide, surface_map);
wl_list_remove(&slide->surface_map.link);
wl_list_init(&slide->surface_map.link);
kde_slide_apply_state(slide);
kywc_log(KYWC_DEBUG, "Surface %p slide map", slide->wlr_surface);
view_apply_slide(slide);
}
static void handle_create(struct wl_client *client, struct wl_resource *manager_resource,
uint32_t id, struct wl_resource *surface_resource)
{
struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource);
struct kde_slide *slide = kde_slide_from_wlr_surface(wlr_surface);
if (!slide) {
slide = calloc(1, sizeof(*slide));
if (!slide) {
wl_client_post_no_memory(client);
return;
}
wl_list_init(&slide->resources);
wl_list_insert(&manager->kde_slides, &slide->link);
slide->wlr_surface = wlr_surface;
slide->surface_map.notify = slide_handle_surface_map;
slide->surface_destroy.notify = slide_handle_surface_destroy;
wl_signal_add(&wlr_surface->events.destroy, &slide->surface_destroy);
if (wlr_surface->mapped) {
wl_list_init(&slide->surface_map.link);
} else {
wl_signal_add_next(&wlr_surface->events.map, &slide->surface_map);
}
}
int version = wl_resource_get_version(manager_resource);
struct wl_resource *resource =
wl_resource_create(client, &org_kde_kwin_slide_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_list_insert(&slide->resources, wl_resource_get_link(resource));
wl_resource_set_implementation(resource, &kde_slide_impl, slide,
kde_slide_handle_resource_destroy);
}
static void handle_unset(struct wl_client *client, struct wl_resource *manager_resource,
struct wl_resource *surface_resource)
{
struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource);
struct kde_slide *slide = kde_slide_from_wlr_surface(wlr_surface);
if (slide) {
kde_slide_destroy(slide);
}
struct view *view = view_try_from_wlr_surface(wlr_surface);
if (view) {
view->use_slide = false;
}
}
static const struct org_kde_kwin_slide_manager_interface kde_slide_manager_impl = {
.create = handle_create,
.unset = handle_unset,
};
static void kde_slide_manager_bind(struct wl_client *client, void *data, uint32_t version,
uint32_t id)
{
struct wl_resource *resource =
wl_resource_create(client, &org_kde_kwin_slide_manager_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &kde_slide_manager_impl, manager, NULL);
}
static void handle_display_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
}
static void handle_server_destroy(struct wl_listener *listener, void *data)
{
wl_list_remove(&manager->server_destroy.link);
free(manager);
manager = NULL;
}
bool kde_slide_manager_create(struct server *server)
{
manager = calloc(1, sizeof(*manager));
if (!manager) {
return false;
}
manager->global =
wl_global_create(server->display, &org_kde_kwin_slide_manager_interface,
KDE_KWIN_SLIDE_MANAGER_VERSION, manager, kde_slide_manager_bind);
if (!manager->global) {
kywc_log(KYWC_WARN, "Kde slide manager create failed");
free(manager);
return false;
}
wl_list_init(&manager->kde_slides);
manager->server_destroy.notify = handle_server_destroy;
server_add_destroy_listener(server, &manager->server_destroy);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(server->display, &manager->display_destroy);
return true;
}
kylin-wayland-compositor/src/view/action.c 0000664 0001750 0001750 00000040072 15160461067 017652 0 ustar feng feng // SPDX-FileCopyrightText: 2023 KylinSoft Co., Ltd.
//
// SPDX-License-Identifier: Expat
#include