Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
58dec1391f | |||
ba331cab53 | |||
54b5fe7e30 | |||
22669edadb | |||
acd95b1664 | |||
63380ae59d | |||
c0cd8e4ac0 | |||
3e7e49de5c |
@ -16,7 +16,7 @@ setups.
|
||||
Build requirements are:
|
||||
|
||||
- meson
|
||||
- GTK+3
|
||||
- GTK4
|
||||
- epoxy
|
||||
- wayland-client
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
project('wdisplays', 'c')
|
||||
project('com.github.cyclopsian.wdisplays', 'c', license: 'MIT', version: '0.9')
|
||||
|
||||
conf = configuration_data()
|
||||
conf.set('app_id', meson.project_name())
|
||||
conf.set('version', meson.project_version())
|
||||
|
||||
subdir('protocol')
|
||||
subdir('resources')
|
||||
|
@ -1,2 +0,0 @@
|
||||
option('kanshi', type: 'feature', value: 'auto', description: 'Enable integration with the kanshi daemon')
|
||||
|
@ -5,41 +5,41 @@ wayland_protos = dependency('wayland-protocols', version: '>=1.17')
|
||||
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
|
||||
|
||||
wayland_scanner_code = generator(
|
||||
wayland_scanner,
|
||||
output: '@BASENAME@-protocol.c',
|
||||
arguments: ['private-code', '@INPUT@', '@OUTPUT@'],
|
||||
wayland_scanner,
|
||||
output: '@BASENAME@-protocol.c',
|
||||
arguments: ['private-code', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
wayland_scanner_client = generator(
|
||||
wayland_scanner,
|
||||
output: '@BASENAME@-client-protocol.h',
|
||||
arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
|
||||
wayland_scanner,
|
||||
output: '@BASENAME@-client-protocol.h',
|
||||
arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
client_protocols = [
|
||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
|
||||
['wlr-output-management-unstable-v1.xml'],
|
||||
['wlr-screencopy-unstable-v1.xml'],
|
||||
['wlr-layer-shell-unstable-v1.xml']
|
||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
|
||||
['wlr-output-management-unstable-v1.xml'],
|
||||
['wlr-screencopy-unstable-v1.xml'],
|
||||
['wlr-layer-shell-unstable-v1.xml']
|
||||
]
|
||||
|
||||
client_protos_src = []
|
||||
client_protos_headers = []
|
||||
|
||||
foreach p : client_protocols
|
||||
xml = join_paths(p)
|
||||
client_protos_src += wayland_scanner_code.process(xml)
|
||||
client_protos_headers += wayland_scanner_client.process(xml)
|
||||
xml = join_paths(p)
|
||||
client_protos_src += wayland_scanner_code.process(xml)
|
||||
client_protos_headers += wayland_scanner_client.process(xml)
|
||||
endforeach
|
||||
|
||||
lib_client_protos = static_library(
|
||||
'client_protos',
|
||||
client_protos_src + client_protos_headers,
|
||||
dependencies: [wayland_client]
|
||||
'client_protos',
|
||||
client_protos_src + client_protos_headers,
|
||||
dependencies: [wayland_client]
|
||||
)
|
||||
|
||||
client_protos = declare_dependency(
|
||||
link_with: lib_client_protos,
|
||||
sources: client_protos_headers,
|
||||
link_with: lib_client_protos,
|
||||
sources: client_protos_headers,
|
||||
)
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.0 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<object class="GtkAdjustment" id="height_adjustment">
|
||||
@ -7,23 +6,6 @@
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkPopover" id="modes">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="mode_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_start">10</property>
|
||||
<property name="margin_end">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="pos_x_adjustment">
|
||||
<property name="upper">16383</property>
|
||||
<property name="step_increment">1</property>
|
||||
@ -51,430 +33,278 @@
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkGrid" id="form">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_start">8</property>
|
||||
<property name="margin_end">8</property>
|
||||
<property name="margin_top">8</property>
|
||||
<property name="margin_bottom">8</property>
|
||||
<property name="row_spacing">8</property>
|
||||
<property name="column_spacing">16</property>
|
||||
<property name="row_homogeneous">True</property>
|
||||
<property name="row_homogeneous">1</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="enabled">
|
||||
<property name="label" translatable="yes">_Enabled</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="enabled" swapped="no"/>
|
||||
<property name="use_underline">1</property>
|
||||
<layout>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="description">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap">1</property>
|
||||
<property name="wrap_mode">word-char</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="xalign">0</property>
|
||||
<layout>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="scale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="width_chars">9</property>
|
||||
<property name="adjustment">scale_adjustment</property>
|
||||
<property name="digits">2</property>
|
||||
<property name="value">1</property>
|
||||
<signal name="change-value" handler="scale" swapped="no"/>
|
||||
<layout>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">DPI _Scale</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_underline">1</property>
|
||||
<property name="mnemonic_widget">scale</property>
|
||||
<property name="xalign">1</property>
|
||||
<layout>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Position</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_underline">1</property>
|
||||
<property name="mnemonic_widget">pos_x</property>
|
||||
<property name="xalign">1</property>
|
||||
<layout>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">4</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Description</property>
|
||||
<property name="xalign">1</property>
|
||||
<layout>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="physical_size">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap">1</property>
|
||||
<property name="wrap_mode">word-char</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="xalign">0</property>
|
||||
<layout>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Physical Size</property>
|
||||
<property name="xalign">1</property>
|
||||
<layout>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Si_ze</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_underline">1</property>
|
||||
<property name="mnemonic_widget">width</property>
|
||||
<property name="xalign">1</property>
|
||||
<layout>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">5</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="refresh">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="name">refresh</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="width_chars">9</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="adjustment">refresh_adjustment</property>
|
||||
<property name="digits">3</property>
|
||||
<property name="numeric">True</property>
|
||||
<property name="numeric">1</property>
|
||||
<property name="update_policy">if-valid</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Hz</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<layout>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">6</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Refresh Rate</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_underline">1</property>
|
||||
<property name="xalign">1</property>
|
||||
<layout>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">6</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuButton" id="rotate_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="popover">transforms</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="receives_default">1</property>
|
||||
<layout>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">7</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">_Transform</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_underline">1</property>
|
||||
<property name="xalign">1</property>
|
||||
<layout>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">7</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="flipped">
|
||||
<property name="label" translatable="yes">_Flipped</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="flipped" swapped="no"/>
|
||||
<property name="use_underline">1</property>
|
||||
<layout>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">8</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="row_spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="pos_x">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="width_chars">6</property>
|
||||
<property name="text" translatable="yes">0</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="adjustment">pos_x_adjustment</property>
|
||||
<property name="numeric">True</property>
|
||||
<property name="numeric">1</property>
|
||||
<property name="update_policy">if-valid</property>
|
||||
<layout>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="pos_y">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="width_chars">6</property>
|
||||
<property name="text" translatable="yes">0</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="adjustment">pos_y_adjustment</property>
|
||||
<property name="numeric">True</property>
|
||||
<property name="numeric">1</property>
|
||||
<property name="update_policy">if-valid</property>
|
||||
<layout>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="top_attach">0</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="width">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="name">width</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="width_chars">4</property>
|
||||
<property name="text" translatable="yes">0</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="adjustment">width_adjustment</property>
|
||||
<property name="numeric">True</property>
|
||||
<property name="numeric">1</property>
|
||||
<property name="update_policy">if-valid</property>
|
||||
<layout>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="width_request">20</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">×</property>
|
||||
<layout>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="height">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="name">height</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="width_chars">4</property>
|
||||
<property name="text" translatable="yes">0</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="adjustment">height_adjustment</property>
|
||||
<property name="numeric">True</property>
|
||||
<property name="numeric">1</property>
|
||||
<property name="update_policy">if-valid</property>
|
||||
<layout>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuButton" id="mode_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="receives_default">1</property>
|
||||
<property name="tooltip_text" translatable="yes">Select Mode Preset</property>
|
||||
<property name="margin_left">8</property>
|
||||
<property name="margin_start">8</property>
|
||||
<property name="popover">modes</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">view-more-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
<property name="icon_name">view-more-symbolic</property>
|
||||
<layout>
|
||||
<property name="left_attach">3</property>
|
||||
<property name="top_attach">1</property>
|
||||
</layout>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">3</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="height">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkPopover" id="transforms">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="relative_to">rotate_button</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_start">10</property>
|
||||
<property name="margin_end">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkModelButton" id="rotate_0">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">transform.rotate_0</property>
|
||||
<property name="text" translatable="yes">Don't Rotate</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton" id="rotate_90">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">transform.rotate_90</property>
|
||||
<property name="text" translatable="yes">Rotate 90°</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton" id="rotate_180">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">transform.rotate_180</property>
|
||||
<property name="text" translatable="yes">Rotate 180°</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton" id="rotate_270">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">transform.rotate_270</property>
|
||||
<property name="text" translatable="yes">Rotate 270°</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<layout>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="row_span">2</property>
|
||||
</layout>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -1,7 +1,39 @@
|
||||
|
||||
gnome = import('gnome')
|
||||
resources = gnome.compile_resources(
|
||||
'waydisplay-resources', 'resources.xml',
|
||||
'wdisplays-resources', 'resources.xml',
|
||||
source_dir : '.',
|
||||
c_name : 'waydisplay_resources')
|
||||
c_name : 'wdisplays_resources')
|
||||
|
||||
scour = find_program('scour', required: false)
|
||||
|
||||
icon = 'wdisplays.svg'
|
||||
icondir = get_option('datadir') / 'icons' / 'hicolor' / 'scalable' / 'apps'
|
||||
|
||||
if scour.found()
|
||||
custom_target('optimize-icon',
|
||||
input: icon,
|
||||
output: '@0@.svg'.format(meson.project_name()),
|
||||
command: [scour,
|
||||
'--enable-viewboxing',
|
||||
'--enable-comment-stripping',
|
||||
'--remove-descriptive-elements',
|
||||
'--enable-id-stripping',
|
||||
'--shorten-ids',
|
||||
'--create-groups',
|
||||
'--strip-xml-space',
|
||||
'--strip-xml-prolog',
|
||||
'--indent=none',
|
||||
'--no-line-breaks',
|
||||
'@INPUT@', '@OUTPUT@'],
|
||||
install: true,
|
||||
install_dir: icondir)
|
||||
else
|
||||
install_data(icon, install_dir: icondir)
|
||||
endif
|
||||
|
||||
install_data(
|
||||
configure_file(input: 'wdisplays.desktop.in',
|
||||
output: '@0@.desktop'.format(meson.project_name()),
|
||||
configuration: conf),
|
||||
install_dir: get_option('datadir') / 'applications')
|
||||
|
7
resources/wdisplays.desktop.in
Normal file
7
resources/wdisplays.desktop.in
Normal file
@ -0,0 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Version=@version@
|
||||
Type=Application
|
||||
Name=wdisplays
|
||||
Comment=Wlroots display configuration
|
||||
Exec=wdisplays
|
||||
Icon=@app_id@
|
662
resources/wdisplays.svg
Normal file
662
resources/wdisplays.svg
Normal file
@ -0,0 +1,662 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-filename="Template.png"
|
||||
width="128"
|
||||
height="128"
|
||||
id="svg11300"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="wdisplays.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.0"
|
||||
style="display:inline;enable-background:new"
|
||||
viewBox="0 0 128 128">
|
||||
<metadata
|
||||
id="metadata96">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs3">
|
||||
<linearGradient
|
||||
id="linearGradient4781"
|
||||
osb:paint="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4779" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1019">
|
||||
<stop
|
||||
style="stop-color:#9a9996;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop1015" />
|
||||
<stop
|
||||
style="stop-color:#3d3846;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop1017" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient11745">
|
||||
<stop
|
||||
style="stop-color:#1a5fb4;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop11741" />
|
||||
<stop
|
||||
id="stop11749"
|
||||
offset="0.04166667"
|
||||
style="stop-color:#1c71d8;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#1a5fb4;stop-opacity:1"
|
||||
offset="0.08333334"
|
||||
id="stop11751" />
|
||||
<stop
|
||||
id="stop11753"
|
||||
offset="0.91666669"
|
||||
style="stop-color:#1a5fb4;stop-opacity:1" />
|
||||
<stop
|
||||
style="stop-color:#1c71d8;stop-opacity:1"
|
||||
offset="0.95833331"
|
||||
id="stop11755" />
|
||||
<stop
|
||||
style="stop-color:#1a5fb4;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop11743" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient11721">
|
||||
<stop
|
||||
style="stop-color:#62a0ea;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop11717" />
|
||||
<stop
|
||||
style="stop-color:#1c71d8;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop11719" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="236"
|
||||
x2="96"
|
||||
y1="236"
|
||||
x1="32"
|
||||
gradientTransform="translate(604.81684,170.58641)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient1099"
|
||||
xlink:href="#linearGradient1036" />
|
||||
<linearGradient
|
||||
id="linearGradient1036">
|
||||
<stop
|
||||
id="stop1032"
|
||||
offset="0"
|
||||
style="stop-color:#d5d3cf;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop1034"
|
||||
offset="1"
|
||||
style="stop-color:#f6f5f4;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="32"
|
||||
fy="-76"
|
||||
fx="-244"
|
||||
cy="-76"
|
||||
cx="-244"
|
||||
gradientTransform="matrix(0.88333331,0,0,0.88333331,-460.35018,463.11973)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1103"
|
||||
xlink:href="#linearGradient1069" />
|
||||
<linearGradient
|
||||
id="linearGradient1069">
|
||||
<stop
|
||||
id="stop1065"
|
||||
offset="0"
|
||||
style="stop-color:#d5d3cf;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop1067-1"
|
||||
offset="1"
|
||||
style="stop-color:#949390;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="232"
|
||||
x2="64"
|
||||
y1="262.5"
|
||||
x1="64"
|
||||
id="linearGradient1027"
|
||||
xlink:href="#linearGradient1025"
|
||||
gradientTransform="translate(-470.5864,432.81685)" />
|
||||
<linearGradient
|
||||
id="linearGradient1025">
|
||||
<stop
|
||||
id="stop1021"
|
||||
offset="0"
|
||||
style="stop-color:#9a9996;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop1023"
|
||||
offset="1"
|
||||
style="stop-color:#77767b;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<inkscape:path-effect
|
||||
effect="spiro"
|
||||
id="path-effect35304-9"
|
||||
is_visible="true" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath1609-7">
|
||||
<path
|
||||
sodipodi:nodetypes="cccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path1611-5"
|
||||
d="m 252,116 28,-28 v -8 h -36 v 36 z"
|
||||
style="fill:#e74747;stroke:none;stroke-width:0.25px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
id="linearGradient1064"
|
||||
x1="88.596001"
|
||||
x2="536.59998"
|
||||
y1="-449.39001"
|
||||
y2="-449.39001"
|
||||
gradientTransform="matrix(0.25,0,0,0.25,-14.149,400.35)"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
id="stop1066"
|
||||
style="stop-color:#5e5c64;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop1068"
|
||||
style="stop-color:#77767b;stop-opacity:1"
|
||||
offset=".035714" />
|
||||
<stop
|
||||
id="stop1070"
|
||||
style="stop-color:#5e5c64;stop-opacity:1"
|
||||
offset=".071365" />
|
||||
<stop
|
||||
id="stop1072"
|
||||
style="stop-color:#5e5c64;stop-opacity:1"
|
||||
offset=".92857" />
|
||||
<stop
|
||||
id="stop1074"
|
||||
style="stop-color:#77767b;stop-opacity:1"
|
||||
offset=".96429" />
|
||||
<stop
|
||||
id="stop1076"
|
||||
style="stop-color:#5e5c64;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1064-9"
|
||||
x1="88.596001"
|
||||
x2="536.59998"
|
||||
y1="-449.39001"
|
||||
y2="-449.39001"
|
||||
gradientTransform="matrix(0.25,0,0,0.25,-14.148999,400.35)"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
id="stop1066-1"
|
||||
style="stop-color:#3d3846;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop1068-2"
|
||||
style="stop-color:#77767b;stop-opacity:1"
|
||||
offset=".035714" />
|
||||
<stop
|
||||
id="stop1070-7"
|
||||
style="stop-color:#5e5c64;stop-opacity:1"
|
||||
offset=".071365" />
|
||||
<stop
|
||||
id="stop1072-0"
|
||||
style="stop-color:#5e5c64;stop-opacity:1"
|
||||
offset=".92857" />
|
||||
<stop
|
||||
id="stop1074-9"
|
||||
style="stop-color:#77767b;stop-opacity:1"
|
||||
offset=".96429" />
|
||||
<stop
|
||||
id="stop1076-3"
|
||||
style="stop-color:#3d3846;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11745"
|
||||
id="linearGradient11747"
|
||||
x1="12"
|
||||
y1="240"
|
||||
x2="60"
|
||||
y2="240"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(2,-10)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11721"
|
||||
id="radialGradient949"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(6.9999999,-3.4009078e-7,2.1255735e-7,4.3749999,-25.345543,-708.76861)"
|
||||
cx="3.6207857"
|
||||
cy="206.80426"
|
||||
fx="3.6207857"
|
||||
fy="206.80426"
|
||||
r="16" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11745"
|
||||
id="linearGradient1001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(54,-10)"
|
||||
x1="12"
|
||||
y1="240"
|
||||
x2="60"
|
||||
y2="240" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11721"
|
||||
id="radialGradient1003"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(6.9999999,-3.4009078e-7,2.1255735e-7,4.3749999,26.654457,-708.76861)"
|
||||
cx="3.6207857"
|
||||
cy="206.80426"
|
||||
fx="3.6207857"
|
||||
fy="206.80426"
|
||||
r="16" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11745"
|
||||
id="linearGradient1011"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(28,28)"
|
||||
x1="12"
|
||||
y1="240"
|
||||
x2="60"
|
||||
y2="240" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11721"
|
||||
id="radialGradient1013"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(6.9999999,-3.4009078e-7,2.1255735e-7,4.3749999,0.654457,-670.76861)"
|
||||
cx="3.6207857"
|
||||
cy="206.80426"
|
||||
fx="3.6207857"
|
||||
fy="206.80426"
|
||||
r="16" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
stroke="#ef2929"
|
||||
fill="#f57900"
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="0.25490196"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="8"
|
||||
inkscape:cx="12.741961"
|
||||
inkscape:cy="57.04762"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1061"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="0"
|
||||
width="400px"
|
||||
height="300px"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-bbox="true"
|
||||
objecttolerance="7"
|
||||
gridtolerance="12"
|
||||
guidetolerance="13"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:pagecheckerboard="false"
|
||||
showguides="false"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:locked="false"
|
||||
inkscape:measure-start="0,0"
|
||||
inkscape:measure-end="0,0"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
showborder="false"
|
||||
inkscape:snap-center="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-text-baseline="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5883"
|
||||
spacingx="2"
|
||||
spacingy="2"
|
||||
enabled="true"
|
||||
visible="true"
|
||||
empspacing="4"
|
||||
originx="0"
|
||||
originy="0" />
|
||||
<sodipodi:guide
|
||||
position="64,8"
|
||||
orientation="0,1"
|
||||
id="guide1073"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="12,64"
|
||||
orientation="1,0"
|
||||
id="guide1075"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="64,104"
|
||||
orientation="0,1"
|
||||
id="guide1099"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="64,128"
|
||||
orientation="0,1"
|
||||
id="guide993"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="104,64"
|
||||
orientation="1,0"
|
||||
id="guide995"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="9.2651362e-08,64"
|
||||
orientation="1,0"
|
||||
id="guide867"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="120,64"
|
||||
orientation="1,0"
|
||||
id="guide869"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="64,116"
|
||||
orientation="0,1"
|
||||
id="guide871"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid873"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
empspacing="8"
|
||||
color="#000000"
|
||||
opacity="0.49019608"
|
||||
empcolor="#000000"
|
||||
empopacity="0.08627451"
|
||||
dotted="true" />
|
||||
<sodipodi:guide
|
||||
position="24,64"
|
||||
orientation="1,0"
|
||||
id="guide877"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="116,64"
|
||||
orientation="1,0"
|
||||
id="guide879"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="64,120"
|
||||
orientation="0,1"
|
||||
id="guide881"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="64,12"
|
||||
orientation="0,1"
|
||||
id="guide883"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="8,64"
|
||||
orientation="1,0"
|
||||
id="guide885"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="128,64"
|
||||
orientation="1,0"
|
||||
id="guide887"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="64,0"
|
||||
orientation="0,1"
|
||||
id="guide897"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="64,24"
|
||||
orientation="0,1"
|
||||
id="guide899"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="256,256"
|
||||
orientation="-0.70710678,0.70710678"
|
||||
id="guide950"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="64,64"
|
||||
orientation="0.70710678,0.70710678"
|
||||
id="guide952"
|
||||
inkscape:locked="false"
|
||||
inkscape:label=""
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
</sodipodi:namedview>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="App Icon"
|
||||
inkscape:groupmode="layer"
|
||||
style="display:inline"
|
||||
transform="translate(0,-172)">
|
||||
<rect
|
||||
style="opacity:1;fill:#deddda;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:0.16205534;paint-order:markers fill stroke"
|
||||
id="rect4815"
|
||||
width="108"
|
||||
height="80"
|
||||
x="10"
|
||||
y="202"
|
||||
ry="6"
|
||||
rx="6" />
|
||||
<rect
|
||||
style="display:inline;fill:url(#linearGradient1064-9);stroke:none;paint-order:normal;enable-background:new"
|
||||
ry="8"
|
||||
rx="8"
|
||||
height="88"
|
||||
width="112"
|
||||
y="200"
|
||||
x="8"
|
||||
id="rect1048" />
|
||||
<rect
|
||||
style="display:inline;opacity:1;fill:#9a9996;fill-opacity:1;stroke:none;stroke-width:0.01;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="rect2392"
|
||||
width="112"
|
||||
height="84"
|
||||
x="8"
|
||||
y="200"
|
||||
rx="8"
|
||||
ry="8" />
|
||||
<rect
|
||||
ry="4"
|
||||
rx="4"
|
||||
y="206"
|
||||
x="14"
|
||||
height="34"
|
||||
width="48"
|
||||
id="rect4520"
|
||||
style="opacity:1;fill:url(#linearGradient11747);fill-opacity:1;stroke:none;stroke-width:0.00999999;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;paint-order:markers fill stroke" />
|
||||
<rect
|
||||
style="opacity:1;fill:url(#radialGradient949);fill-opacity:1;stroke:none;stroke-width:0.00999999;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="rect947"
|
||||
width="48"
|
||||
height="32"
|
||||
x="14"
|
||||
y="206"
|
||||
rx="4"
|
||||
ry="4" />
|
||||
<rect
|
||||
style="opacity:1;fill:#99c1f1;fill-opacity:0.21912351;stroke:none;stroke-width:0.01;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="rect937"
|
||||
width="44"
|
||||
height="28"
|
||||
x="16"
|
||||
y="208"
|
||||
rx="2"
|
||||
ry="2" />
|
||||
<rect
|
||||
style="opacity:1;fill:url(#linearGradient1001);fill-opacity:1;stroke:none;stroke-width:0.00999999;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="rect995"
|
||||
width="48"
|
||||
height="34"
|
||||
x="66"
|
||||
y="206"
|
||||
rx="4"
|
||||
ry="4" />
|
||||
<rect
|
||||
ry="4"
|
||||
rx="4"
|
||||
y="206"
|
||||
x="66"
|
||||
height="32"
|
||||
width="48"
|
||||
id="rect997"
|
||||
style="opacity:1;fill:url(#radialGradient1003);fill-opacity:1;stroke:none;stroke-width:0.00999999;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;paint-order:markers fill stroke" />
|
||||
<rect
|
||||
ry="2"
|
||||
rx="2"
|
||||
y="208"
|
||||
x="68"
|
||||
height="28"
|
||||
width="44"
|
||||
id="rect999"
|
||||
style="opacity:1;fill:#99c1f1;fill-opacity:0.21912351;stroke:none;stroke-width:0.01;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;paint-order:markers fill stroke" />
|
||||
<rect
|
||||
ry="4"
|
||||
rx="4"
|
||||
y="244"
|
||||
x="40"
|
||||
height="34"
|
||||
width="48"
|
||||
id="rect1005"
|
||||
style="opacity:1;fill:url(#linearGradient1011);fill-opacity:1;stroke:none;stroke-width:0.00999999;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;paint-order:markers fill stroke" />
|
||||
<rect
|
||||
style="opacity:1;fill:url(#radialGradient1013);fill-opacity:1;stroke:none;stroke-width:0.00999999;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="rect1007"
|
||||
width="48"
|
||||
height="32"
|
||||
x="40"
|
||||
y="244"
|
||||
rx="4"
|
||||
ry="4" />
|
||||
<rect
|
||||
style="opacity:1;fill:#99c1f1;fill-opacity:0.21912351;stroke:none;stroke-width:0.01;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="rect1009"
|
||||
width="44"
|
||||
height="28"
|
||||
x="42"
|
||||
y="246"
|
||||
rx="2"
|
||||
ry="2" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4777"
|
||||
d="M 14,242 H 114"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:4;stroke-opacity:0.16205534" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:4;stroke-opacity:0.16205534"
|
||||
d="m 64,206 v 34"
|
||||
id="path4785"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4787"
|
||||
d="m 38,244 v 34"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:4;stroke-opacity:0.16205534"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:4;stroke-opacity:0.16205534"
|
||||
d="m 90,244 v 34"
|
||||
id="path4789"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:4;stroke-opacity:0.16205534"
|
||||
d="M 10,204 H 118"
|
||||
id="path4791"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4793"
|
||||
d="M 10,280 H 118"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:4;stroke-opacity:0.16205534" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4795"
|
||||
d="m 12,206 v 72"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:6;stroke-opacity:0.16205534"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
id="path4797"
|
||||
sodipodi:nodetypes="cc"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:6;stroke-opacity:0.16205534"
|
||||
d="m 116,206 v 72"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 21 KiB |
@ -1,8 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.0 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<object class="GtkAdjustment" id="canvas_horiz">
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
@ -11,143 +9,56 @@
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkPopover" id="main_menu">
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_start">10</property>
|
||||
<property name="margin_end">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">app.auto-apply</property>
|
||||
<property name="text" translatable="yes">_Automatically Apply Changes</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">app.capture-screens</property>
|
||||
<property name="text" translatable="yes">Show Screen Contents</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">app.show-overlay</property>
|
||||
<property name="text" translatable="yes">Overlay Screen Names</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkWindow" id="heads_window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Waydisplay</property>
|
||||
<signal name="destroy" handler="destroy" swapped="no"/>
|
||||
<property name="title" translatable="yes">wdisplays</property>
|
||||
<child>
|
||||
<object class="GtkOverlay" id="overlay">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="heads_info">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="visible">0</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="message_type">error</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<property name="revealed">False</property>
|
||||
<signal name="response" handler="info_response" swapped="no"/>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="show_close_button">1</property>
|
||||
<property name="revealed">0</property>
|
||||
<child type="action">
|
||||
<object class="GtkBox">
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="visible">0</property>
|
||||
<property name="spacing">16</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="heads_info_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="wrap">1</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<object class="GtkPaned" id="paned">
|
||||
<property name="resize_child2">0</property>
|
||||
<property name="shrink_child1">0</property>
|
||||
<property name="shrink_child2">0</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="position">400</property>
|
||||
<property name="position_set">True</property>
|
||||
<property name="position_set">1</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="heads_scroll">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="hadjustment">canvas_horiz</property>
|
||||
<property name="vadjustment">canvas_vert</property>
|
||||
<property name="min_content_width">400</property>
|
||||
@ -156,232 +67,136 @@
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkStackSwitcher" id="heads_stack_switcher">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="margin_left">8</property>
|
||||
<property name="margin_right">8</property>
|
||||
<property name="margin_start">8</property>
|
||||
<property name="margin_end">8</property>
|
||||
<property name="margin_top">8</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="hexpand">1</property>
|
||||
<property name="stack">heads_stack</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkStack" id="heads_stack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="transition_type">crossfade</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="index">-1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkSpinner" id="spinner">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="visible">0</property>
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<property name="can_target">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pass_through">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="titlebar">
|
||||
<object class="GtkStack" id="header_stack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="transition_type">crossfade</property>
|
||||
<child>
|
||||
<object class="GtkHeaderBar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">wdisplays</property>
|
||||
<property name="has_subtitle">False</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="zoom_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">title</property>
|
||||
<property name="child">
|
||||
<object class="GtkHeaderBar">
|
||||
<property name="title" translatable="yes">wdisplays</property>
|
||||
<property name="has_subtitle">0</property>
|
||||
<property name="show_title_buttons">1</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="zoom_out">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Zoom Out</property>
|
||||
<signal name="clicked" handler="zoom_out" swapped="yes"/>
|
||||
<object class="GtkBox" id="zoom_box">
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<object class="GtkButton" id="zoom_out">
|
||||
<property name="can_focus">1</property>
|
||||
<property name="receives_default">1</property>
|
||||
<property name="tooltip_text" translatable="yes">Zoom Out</property>
|
||||
<property name="action_name">app.zoom-out</property>
|
||||
<property name="icon_name">zoom-out-symbolic</property>
|
||||
<accelerator key="minus" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="minus" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="non_homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="zoom_reset">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Zoom Reset</property>
|
||||
<signal name="clicked" handler="zoom_reset" swapped="yes"/>
|
||||
<accelerator key="0" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="non_homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="zoom_in">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Zoom In</property>
|
||||
<signal name="clicked" handler="zoom_in" swapped="yes"/>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">zoom-in-symbolic</property>
|
||||
<object class="GtkButton" id="zoom_reset">
|
||||
<property name="can_focus">1</property>
|
||||
<property name="receives_default">1</property>
|
||||
<property name="tooltip_text" translatable="yes">Zoom Reset</property>
|
||||
<property name="action_name">app.zoom-reset</property>
|
||||
<accelerator key="0" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="equal" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
<child>
|
||||
<object class="GtkButton" id="zoom_in">
|
||||
<property name="can_focus">1</property>
|
||||
<property name="receives_default">1</property>
|
||||
<property name="tooltip_text" translatable="yes">Zoom In</property>
|
||||
<property name="action_name">app.zoom-in</property>
|
||||
<property name="icon_name">zoom-in-symbolic</property>
|
||||
<accelerator key="equal" signal="clicked" modifiers="GDK_CONTROL_MASK"/>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="linked"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="non_homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuButton" id="menu_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="popover">main_menu</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child type="end">
|
||||
<object class="GtkMenuButton" id="menu_button">
|
||||
<property name="can_focus">1</property>
|
||||
<property name="receives_default">1</property>
|
||||
<property name="icon_name">open-menu-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">title</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHeaderBar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child type="title">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Apply Changes?</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="label" translatable="yes">_Apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="apply_changes" swapped="no"/>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="label" translatable="yes">_Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="cancel_changes" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<object class="GtkStackPage">
|
||||
<property name="name">apply</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
<property name="child">
|
||||
<object class="GtkHeaderBar">
|
||||
<child type="title">
|
||||
<object class="GtkLabel">
|
||||
<property name="label" translatable="yes">Apply Changes?</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="label" translatable="yes">_Apply</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="receives_default">1</property>
|
||||
<property name="use_underline">1</property>
|
||||
<property name="action_name">app.apply-changes</property>
|
||||
<style>
|
||||
<class name="suggested-action"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkButton">
|
||||
<property name="label" translatable="yes">_Cancel</property>
|
||||
<property name="can_focus">1</property>
|
||||
<property name="receives_default">1</property>
|
||||
<property name="use_underline">1</property>
|
||||
<property name="action_name">app.cancel-changes</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
10
src/config.h.in
Normal file
10
src/config.h.in
Normal file
@ -0,0 +1,10 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright (C) 2019 cyclopsian */
|
||||
|
||||
#ifndef WDISPLAY_CONFIG_H
|
||||
#define WDISPLAY_CONFIG_H
|
||||
|
||||
#define WDISPLAYS_APP_ID "@app_id@"
|
||||
#define WDISPLAYS_VERSION "@version@"
|
||||
|
||||
#endif
|
@ -1,25 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 cyclopsian
|
||||
|
||||
* 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 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 X CONSORTIUM 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.
|
||||
*/
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright (C) 2019 cyclopsian */
|
||||
|
||||
#include "glviewport.h"
|
||||
|
||||
|
@ -1,25 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 cyclopsian
|
||||
|
||||
* 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 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 X CONSORTIUM 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.
|
||||
*/
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright (C) 2019 cyclopsian */
|
||||
|
||||
#ifndef WDISPLAY_GLVIEWPORT_H
|
||||
#define WDISPLAY_GLVIEWPORT_H
|
||||
|
95
src/kanshi.h
95
src/kanshi.h
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2019 emersion
|
||||
* Copyright (C) 2019 cyclopsian
|
||||
|
||||
* 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 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 X CONSORTIUM 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Parts of this file are taken from emersion/kanshi:
|
||||
* https://github.com/emersion/kanshi/blob/38d27474b686fcc8324cc5e454741a49577c0988/include/config.h
|
||||
* https://github.com/emersion/kanshi/blob/38d27474b686fcc8324cc5e454741a49577c0988/include/parser.h
|
||||
*/
|
||||
|
||||
#ifndef WDISPLAYS_KANSHI_H
|
||||
#define WDISPLAYS_KANSHI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
enum kanshi_output_field {
|
||||
KANSHI_OUTPUT_ENABLED = 1 << 0,
|
||||
KANSHI_OUTPUT_MODE = 1 << 1,
|
||||
KANSHI_OUTPUT_POSITION = 1 << 2,
|
||||
KANSHI_OUTPUT_SCALE = 1 << 3,
|
||||
KANSHI_OUTPUT_TRANSFORM = 1 << 4,
|
||||
};
|
||||
|
||||
struct kanshi_profile_output {
|
||||
char *name;
|
||||
unsigned int fields; // enum kanshi_output_field
|
||||
struct wl_list link;
|
||||
|
||||
bool enabled;
|
||||
struct {
|
||||
int width, height;
|
||||
int refresh; // mHz
|
||||
} mode;
|
||||
struct {
|
||||
int x, y;
|
||||
} position;
|
||||
float scale;
|
||||
enum wl_output_transform transform;
|
||||
};
|
||||
|
||||
|
||||
struct kanshi_profile_command {
|
||||
struct wl_list link;
|
||||
char *command;
|
||||
};
|
||||
|
||||
struct kanshi_profile {
|
||||
struct wl_list link;
|
||||
char *name;
|
||||
// Wildcard outputs are stored at the end of the list
|
||||
struct wl_list commands;
|
||||
struct wl_list outputs;
|
||||
};
|
||||
|
||||
struct kanshi_config {
|
||||
struct wl_list profiles;
|
||||
};
|
||||
|
||||
/*
|
||||
* Loads the kanshi config from the given file.
|
||||
*/
|
||||
struct kanshi_config *kanshi_parse_config(const char *path);
|
||||
|
||||
/*
|
||||
* Saves the kanshi config to the given file.
|
||||
*/
|
||||
void kanshi_save_config(const char *path, struct kanshi_config *config);
|
||||
|
||||
/*
|
||||
* Destroys the config structure.
|
||||
*/
|
||||
void kanshi_destroy_config(struct kanshi_config *config);
|
||||
|
||||
#endif
|
645
src/main.c
645
src/main.c
@ -1,28 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2019 cyclopsian
|
||||
|
||||
* 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 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 X CONSORTIUM 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.
|
||||
*/
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright (C) 2019 cyclopsian */
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkwayland.h>
|
||||
#include <gdk/wayland/gdkwayland.h>
|
||||
|
||||
#include "wdisplays.h"
|
||||
#include "glviewport.h"
|
||||
@ -40,22 +20,18 @@ __attribute__((noreturn)) void wd_fatal_error(int status, const char *message) {
|
||||
#define MAX_ZOOM 1000.
|
||||
#define CANVAS_MARGIN 40
|
||||
|
||||
static const char *HEAD_PREFIX = "head";
|
||||
static const char *MODE_PREFIX = "mode";
|
||||
static const char *TRANSFORM_PREFIX = "transform";
|
||||
static const char *ROTATE_PREFIX = "rotate";
|
||||
static const char *APP_PREFIX = "app";
|
||||
|
||||
#define NUM_ROTATIONS 4
|
||||
static const char *ROTATE_IDS[NUM_ROTATIONS] = {
|
||||
"rotate_0", "rotate_90", "rotate_180", "rotate_270"
|
||||
};
|
||||
|
||||
static int get_rotate_index(enum wl_output_transform transform) {
|
||||
static int32_t get_rotate_value(enum wl_output_transform transform) {
|
||||
if (transform == WL_OUTPUT_TRANSFORM_90 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_90) {
|
||||
return 1;
|
||||
return 90;
|
||||
} else if (transform == WL_OUTPUT_TRANSFORM_180 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_180) {
|
||||
return 2;
|
||||
return 180;
|
||||
} else if (transform == WL_OUTPUT_TRANSFORM_270 || transform == WL_OUTPUT_TRANSFORM_FLIPPED_270) {
|
||||
return 3;
|
||||
return 270;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -65,6 +41,7 @@ static bool has_changes(const struct wd_state *state) {
|
||||
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
|
||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form_iter->data), "builder"));
|
||||
const struct wd_head *head = g_object_get_data(G_OBJECT(form_iter->data), "head");
|
||||
GAction *rotate_action = G_ACTION(g_object_get_data(G_OBJECT(form_iter->data), "rotate_action"));
|
||||
if (head->enabled != gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "enabled")))) {
|
||||
return TRUE;
|
||||
}
|
||||
@ -91,16 +68,8 @@ static bool has_changes(const struct wd_state *state) {
|
||||
if (r / 1000. != gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "refresh")))) {
|
||||
return TRUE;
|
||||
}
|
||||
for (int i = 0; i < NUM_ROTATIONS; i++) {
|
||||
GtkWidget *rotate = GTK_WIDGET(gtk_builder_get_object(builder, ROTATE_IDS[i]));
|
||||
gboolean selected;
|
||||
g_object_get(rotate, "active", &selected, NULL);
|
||||
if (selected) {
|
||||
if (i != get_rotate_index(head->transform)) {
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (g_variant_get_int32(g_action_get_state(rotate_action)) != get_rotate_value(head->transform)) {
|
||||
return TRUE;
|
||||
}
|
||||
bool flipped = head->transform == WL_OUTPUT_TRANSFORM_FLIPPED
|
||||
|| head->transform == WL_OUTPUT_TRANSFORM_FLIPPED_90
|
||||
@ -123,20 +92,13 @@ void fill_output_from_form(struct wd_head_config *output, GtkWidget *form) {
|
||||
output->width = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "width")));
|
||||
output->height = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "height")));
|
||||
output->refresh = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "refresh"))) * 1000.;
|
||||
int32_t rotate = g_variant_get_int32(g_action_get_state(G_ACTION(g_object_get_data(G_OBJECT(form), "rotate_action"))));
|
||||
gboolean flipped = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "flipped")));
|
||||
for (int i = 0; i < NUM_ROTATIONS; i++) {
|
||||
GtkWidget *rotate = GTK_WIDGET(gtk_builder_get_object(builder, ROTATE_IDS[i]));
|
||||
gboolean selected;
|
||||
g_object_get(rotate, "active", &selected, NULL);
|
||||
if (selected) {
|
||||
switch (i) {
|
||||
case 0: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED : WL_OUTPUT_TRANSFORM_NORMAL; break;
|
||||
case 1: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_90 : WL_OUTPUT_TRANSFORM_90; break;
|
||||
case 2: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_180 : WL_OUTPUT_TRANSFORM_180; break;
|
||||
case 3: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_270 : WL_OUTPUT_TRANSFORM_270; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (rotate) {
|
||||
case 0: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED : WL_OUTPUT_TRANSFORM_NORMAL; break;
|
||||
case 90: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_90 : WL_OUTPUT_TRANSFORM_90; break;
|
||||
case 180: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_180 : WL_OUTPUT_TRANSFORM_180; break;
|
||||
case 270: output->transform = flipped ? WL_OUTPUT_TRANSFORM_FLIPPED_270 : WL_OUTPUT_TRANSFORM_270; break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,8 +113,8 @@ static gboolean send_apply(gpointer data) {
|
||||
wl_list_insert(outputs, &output->link);
|
||||
fill_output_from_form(output, GTK_WIDGET(form_iter->data));
|
||||
}
|
||||
GdkWindow *window = gtk_widget_get_window(state->stack);
|
||||
GdkDisplay *display = gdk_window_get_display(window);
|
||||
GdkSurface *surface = gtk_native_get_surface(gtk_widget_get_native(state->stack));
|
||||
GdkDisplay *display = gdk_surface_get_display(surface);
|
||||
struct wl_display *wl_display = gdk_wayland_display_get_wl_display(display);
|
||||
wd_apply_state(state, outputs, wl_display);
|
||||
state->apply_pending = FALSE;
|
||||
@ -163,7 +125,7 @@ static void apply_state(struct wd_state *state) {
|
||||
gtk_stack_set_visible_child_name(GTK_STACK(state->header_stack), "title");
|
||||
if (!state->autoapply) {
|
||||
gtk_style_context_add_class(gtk_widget_get_style_context(state->spinner), "visible");
|
||||
gtk_overlay_set_overlay_pass_through(GTK_OVERLAY(state->overlay), state->spinner, FALSE);
|
||||
gtk_widget_set_can_target(state->spinner, TRUE);
|
||||
gtk_spinner_start(GTK_SPINNER(state->spinner));
|
||||
|
||||
gtk_widget_set_sensitive(state->stack_switcher, FALSE);
|
||||
@ -293,15 +255,15 @@ static void update_cursor(struct wd_state *state) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
GdkWindow *window = gtk_widget_get_window(state->canvas);
|
||||
GdkSurface *surface = gtk_native_get_surface(gtk_widget_get_native(state->canvas));
|
||||
if (any_hovered) {
|
||||
gdk_window_set_cursor(window, state->grab_cursor);
|
||||
gdk_surface_set_cursor(surface, state->grab_cursor);
|
||||
} else if (state->clicked != NULL) {
|
||||
gdk_window_set_cursor(window, state->grabbing_cursor);
|
||||
gdk_surface_set_cursor(surface, state->grabbing_cursor);
|
||||
} else if (state->panning) {
|
||||
gdk_window_set_cursor(window, state->move_cursor);
|
||||
gdk_surface_set_cursor(surface, state->move_cursor);
|
||||
} else {
|
||||
gdk_window_set_cursor(window, NULL);
|
||||
gdk_surface_set_cursor(surface, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,15 +276,13 @@ static inline void flip_anim(uint64_t *timer, uint64_t tick) {
|
||||
}
|
||||
}
|
||||
|
||||
static void update_hovered(struct wd_state *state) {
|
||||
GdkDisplay *display = gdk_display_get_default();
|
||||
GdkWindow *window = gtk_widget_get_window(state->canvas);
|
||||
static void update_hovered(struct wd_state *state,
|
||||
gdouble mouse_x, gdouble mouse_y) {
|
||||
if (!gtk_widget_get_realized(state->canvas)) {
|
||||
return;
|
||||
}
|
||||
GdkFrameClock *clock = gtk_widget_get_frame_clock(state->canvas);
|
||||
uint64_t tick = gdk_frame_clock_get_frame_time(clock);
|
||||
g_autoptr(GList) seats = gdk_display_list_seats(display);
|
||||
bool any_hovered = FALSE;
|
||||
struct wd_render_head_data *render;
|
||||
wl_list_for_each(render, &state->render.heads, link) {
|
||||
@ -335,18 +295,10 @@ static void update_hovered(struct wd_state *state) {
|
||||
render->hovered = TRUE;
|
||||
any_hovered = TRUE;
|
||||
} else if (state->clicked == NULL) {
|
||||
for (GList *iter = seats; iter != NULL; iter = iter->next) {
|
||||
double mouse_x;
|
||||
double mouse_y;
|
||||
|
||||
GdkDevice *pointer = gdk_seat_get_pointer(GDK_SEAT(iter->data));
|
||||
gdk_window_get_device_position_double(window, pointer, &mouse_x, &mouse_y, NULL);
|
||||
if (mouse_x >= render->x1 && mouse_x < render->x2 &&
|
||||
mouse_y >= render->y1 && mouse_y < render->y2) {
|
||||
render->hovered = TRUE;
|
||||
any_hovered = TRUE;
|
||||
break;
|
||||
}
|
||||
if (mouse_x >= render->x1 && mouse_x < render->x2 &&
|
||||
mouse_y >= render->y1 && mouse_y < render->y2) {
|
||||
render->hovered = TRUE;
|
||||
any_hovered = TRUE;
|
||||
}
|
||||
}
|
||||
if (init_hovered != render->hovered) {
|
||||
@ -368,18 +320,8 @@ static inline void color_to_float_array(GtkStyleContext *ctx,
|
||||
}
|
||||
|
||||
static unsigned form_get_rotation(GtkWidget *form) {
|
||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
|
||||
unsigned rot;
|
||||
for (rot = 0; rot < NUM_ROTATIONS; rot++) {
|
||||
GtkWidget *rotate = GTK_WIDGET(gtk_builder_get_object(builder,
|
||||
ROTATE_IDS[rot]));
|
||||
gboolean selected;
|
||||
g_object_get(rotate, "active", &selected, NULL);
|
||||
if (selected) {
|
||||
return rot;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
int32_t rotate = g_variant_get_int32(g_action_get_state(G_ACTION(g_object_get_data(G_OBJECT(form), "rotate_action"))));
|
||||
return rotate / 90;
|
||||
}
|
||||
|
||||
#define SWAP(_type, _a, _b) { _type _tmp = (_a); (_a) = (_b); (_b) = _tmp; }
|
||||
@ -463,67 +405,81 @@ static void update_sensitivity(GtkWidget *form) {
|
||||
}
|
||||
}
|
||||
|
||||
static void select_rotate_option(GtkWidget *form, GtkWidget *model_button) {
|
||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
|
||||
static void rotate_selected(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
const struct wd_head *head = g_object_get_data(G_OBJECT(data), "head");
|
||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(data, "builder"));
|
||||
GtkWidget *rotate_button = GTK_WIDGET(gtk_builder_get_object(builder, "rotate_button"));
|
||||
for (int i = 0; i < NUM_ROTATIONS; i++) {
|
||||
GtkWidget *rotate = GTK_WIDGET(gtk_builder_get_object(builder, ROTATE_IDS[i]));
|
||||
gboolean selected = model_button == rotate;
|
||||
g_object_set(rotate, "active", selected, NULL);
|
||||
if (selected) {
|
||||
g_autofree gchar *rotate_text = NULL;
|
||||
g_object_get(rotate, "text", &rotate_text, NULL);
|
||||
gtk_button_set_label(GTK_BUTTON(rotate_button), rotate_text);
|
||||
GMenuModel *menu = gtk_menu_button_get_menu_model(GTK_MENU_BUTTON(rotate_button));
|
||||
int items = g_menu_model_get_n_items(menu);
|
||||
for (int i = 0; i < items; i++) {
|
||||
g_autoptr(GVariant) target = g_menu_model_get_item_attribute_value(menu, i, G_MENU_ATTRIBUTE_TARGET, NULL);
|
||||
g_autoptr(GVariant) label = g_menu_model_get_item_attribute_value(menu, i, G_MENU_ATTRIBUTE_LABEL, NULL);
|
||||
if (g_variant_get_int32(target) == g_variant_get_int32(param)) {
|
||||
gtk_menu_button_set_label(GTK_MENU_BUTTON(rotate_button), g_variant_get_string(label, NULL));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rotate_selected(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
select_rotate_option(GTK_WIDGET(data), g_object_get_data(G_OBJECT(action), "widget"));
|
||||
const struct wd_head *head = g_object_get_data(G_OBJECT(data), "head");
|
||||
g_simple_action_set_state(action, param);
|
||||
update_ui(head->state);
|
||||
}
|
||||
|
||||
static void select_mode_option(GtkWidget *form, int32_t w, int32_t h, int32_t r) {
|
||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
|
||||
GtkWidget *mode_box = GTK_WIDGET(gtk_builder_get_object(builder, "mode_box"));
|
||||
g_autoptr(GList) children = gtk_container_get_children(GTK_CONTAINER(mode_box));
|
||||
for (GList *child = children; child != NULL; child = child->next) {
|
||||
const struct wd_mode *mode = g_object_get_data(G_OBJECT(child->data), "mode");
|
||||
g_object_set(child->data, "active", w == mode->width && h == mode->height && r == mode->refresh, NULL);
|
||||
}
|
||||
static GVariant *create_mode_variant(int32_t w, int32_t h, int32_t r) {
|
||||
GVariant * const children[] = {
|
||||
g_variant_new_int32(w),
|
||||
g_variant_new_int32(h),
|
||||
g_variant_new_int32(r),
|
||||
};
|
||||
return g_variant_new_tuple(children, G_N_ELEMENTS(children));
|
||||
}
|
||||
|
||||
static void update_mode_entries(GtkWidget *form, int32_t w, int32_t h, int32_t r) {
|
||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
|
||||
GtkWidget *width = GTK_WIDGET(gtk_builder_get_object(builder, "width"));
|
||||
GtkWidget *height = GTK_WIDGET(gtk_builder_get_object(builder, "height"));
|
||||
GtkWidget *refresh = GTK_WIDGET(gtk_builder_get_object(builder, "refresh"));
|
||||
struct vid_mode {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t refresh;
|
||||
};
|
||||
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(width), w);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(height), h);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(refresh), r / 1000.);
|
||||
static void unpack_mode_variant(GVariant *value, struct vid_mode *mode) {
|
||||
g_autoptr(GVariant) width = g_variant_get_child_value(value, 0);
|
||||
mode->width = g_variant_get_int32(width);
|
||||
g_autoptr(GVariant) height = g_variant_get_child_value(value, 1);
|
||||
mode->height = g_variant_get_int32(height);
|
||||
g_autoptr(GVariant) refresh = g_variant_get_child_value(value, 2);
|
||||
mode->refresh = g_variant_get_int32(refresh);
|
||||
}
|
||||
|
||||
static void mode_spin_changed(GtkSpinButton *spin_button, gpointer data) {
|
||||
GtkWidget *form = data;
|
||||
const struct wd_head *head = g_object_get_data(G_OBJECT(form), "head");
|
||||
struct vid_mode mode;
|
||||
GAction *mode_action = G_ACTION(g_object_get_data(G_OBJECT(form), "mode_action"));
|
||||
GVariant *value = g_action_get_state(mode_action);
|
||||
unpack_mode_variant(value, &mode);
|
||||
if (strcmp(gtk_widget_get_name(GTK_WIDGET(spin_button)), "width") == 0) {
|
||||
mode.width = gtk_spin_button_get_value(spin_button);
|
||||
} else if (strcmp(gtk_widget_get_name(GTK_WIDGET(spin_button)), "height") == 0) {
|
||||
mode.height = gtk_spin_button_get_value(spin_button);
|
||||
} else if (strcmp(gtk_widget_get_name(GTK_WIDGET(spin_button)), "refresh") == 0) {
|
||||
mode.refresh = gtk_spin_button_get_value(spin_button) * 1000.;
|
||||
}
|
||||
g_action_activate(mode_action, create_mode_variant(mode.width, mode.height, mode.refresh));
|
||||
update_ui(head->state);
|
||||
}
|
||||
|
||||
static void mode_selected(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
GtkWidget *form = data;
|
||||
const struct wd_head *head = g_object_get_data(G_OBJECT(form), "head");
|
||||
const struct wd_mode *mode = g_object_get_data(G_OBJECT(action), "mode");
|
||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
|
||||
struct vid_mode mode;
|
||||
unpack_mode_variant(param, &mode);
|
||||
|
||||
update_mode_entries(form, mode->width, mode->height, mode->refresh);
|
||||
select_mode_option(form, mode->width, mode->height, mode->refresh);
|
||||
g_simple_action_set_state(action, param);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "width")), mode.width);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "height")), mode.height);
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "refresh")), mode.refresh / 1000.);
|
||||
update_ui(head->state);
|
||||
}
|
||||
// END FORM CALLBACKS
|
||||
|
||||
static void clear_menu(GtkWidget *box, GActionMap *action_map) {
|
||||
g_autoptr(GList) children = gtk_container_get_children(GTK_CONTAINER(box));
|
||||
for (GList *child = children; child != NULL; child = child->next) {
|
||||
g_action_map_remove_action(action_map, strchr(gtk_actionable_get_action_name(GTK_ACTIONABLE(child->data)), '.') + 1);
|
||||
gtk_container_remove(GTK_CONTAINER(box), GTK_WIDGET(child->data));
|
||||
}
|
||||
}
|
||||
|
||||
static void update_head_form(GtkWidget *form, unsigned int fields) {
|
||||
GtkBuilder *builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form), "builder"));
|
||||
GtkWidget *description = GTK_WIDGET(gtk_builder_get_object(builder, "description"));
|
||||
@ -532,12 +488,14 @@ static void update_head_form(GtkWidget *form, unsigned int fields) {
|
||||
GtkWidget *scale = GTK_WIDGET(gtk_builder_get_object(builder, "scale"));
|
||||
GtkWidget *pos_x = GTK_WIDGET(gtk_builder_get_object(builder, "pos_x"));
|
||||
GtkWidget *pos_y = GTK_WIDGET(gtk_builder_get_object(builder, "pos_y"));
|
||||
GtkWidget *mode_box = GTK_WIDGET(gtk_builder_get_object(builder, "mode_box"));
|
||||
GtkWidget *mode_button = GTK_WIDGET(gtk_builder_get_object(builder, "mode_button"));
|
||||
GtkWidget *flipped = GTK_WIDGET(gtk_builder_get_object(builder, "flipped"));
|
||||
const struct wd_head *head = g_object_get_data(G_OBJECT(form), "head");
|
||||
GAction *mode_action = G_ACTION(g_object_get_data(G_OBJECT(form), "mode_action"));
|
||||
GAction *rotate_action = G_ACTION(g_object_get_data(G_OBJECT(form), "rotate_action"));
|
||||
|
||||
if (fields & WD_FIELD_NAME) {
|
||||
gtk_container_child_set(GTK_CONTAINER(head->state->stack), form, "title", head->name, NULL);
|
||||
g_object_set(gtk_stack_get_page(GTK_STACK(head->state->stack), form), "title", head->name, NULL);
|
||||
}
|
||||
if (fields & WD_FIELD_DESCRIPTION) {
|
||||
gtk_label_set_text(GTK_LABEL(description), head->description);
|
||||
@ -558,27 +516,17 @@ static void update_head_form(GtkWidget *form, unsigned int fields) {
|
||||
}
|
||||
|
||||
if (fields & WD_FIELD_MODE) {
|
||||
GActionMap *mode_actions = G_ACTION_MAP(g_object_get_data(G_OBJECT(form), "mode-group"));
|
||||
clear_menu(mode_box, mode_actions);
|
||||
GMenu *mode_menu = g_menu_new();
|
||||
struct wd_mode *mode;
|
||||
g_autofree gchar *action = g_strdup_printf("%s.%s", HEAD_PREFIX, MODE_PREFIX);
|
||||
wl_list_for_each(mode, &head->modes, link) {
|
||||
g_autofree gchar *name = g_strdup_printf("%d×%d@%0.3fHz", mode->width, mode->height, mode->refresh / 1000.);
|
||||
GSimpleAction *action = g_simple_action_new(name, NULL);
|
||||
g_action_map_add_action(G_ACTION_MAP(mode_actions), G_ACTION(action));
|
||||
g_signal_connect(action, "activate", G_CALLBACK(mode_selected), form);
|
||||
g_object_set_data(G_OBJECT(action), "mode", mode);
|
||||
g_object_unref(action);
|
||||
|
||||
GtkWidget *button = gtk_model_button_new();
|
||||
g_autoptr(GString) prefixed_name = g_string_new(MODE_PREFIX);
|
||||
g_string_append(prefixed_name, ".");
|
||||
g_string_append(prefixed_name, name);
|
||||
gtk_actionable_set_action_name(GTK_ACTIONABLE(button), prefixed_name->str);
|
||||
g_object_set(button, "role", GTK_BUTTON_ROLE_RADIO, "text", name, NULL);
|
||||
gtk_box_pack_start(GTK_BOX(mode_box), button, FALSE, FALSE, 0);
|
||||
g_object_set_data(G_OBJECT(button), "mode", mode);
|
||||
gtk_widget_show_all(button);
|
||||
GMenuItem *item = g_menu_item_new(name, action);
|
||||
g_menu_item_set_attribute_value(item, G_MENU_ATTRIBUTE_TARGET,
|
||||
create_mode_variant(mode->width, mode->height, mode->refresh));
|
||||
g_menu_append_item(mode_menu, item);
|
||||
}
|
||||
gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(mode_button), G_MENU_MODEL(mode_menu));
|
||||
// Mode entries
|
||||
int w = head->custom_mode.width;
|
||||
int h = head->custom_mode.height;
|
||||
@ -599,14 +547,12 @@ static void update_head_form(GtkWidget *form, unsigned int fields) {
|
||||
}
|
||||
}
|
||||
|
||||
update_mode_entries(form, w, h, r);
|
||||
select_mode_option(form, w, h, r);
|
||||
gtk_widget_show_all(mode_box);
|
||||
g_action_change_state(mode_action, create_mode_variant(w, h, r));
|
||||
}
|
||||
|
||||
if (fields & WD_FIELD_TRANSFORM) {
|
||||
int active_rotate = get_rotate_index(head->transform);
|
||||
select_rotate_option(form, GTK_WIDGET(gtk_builder_get_object(builder, ROTATE_IDS[active_rotate])));
|
||||
int active_rotate = get_rotate_value(head->transform);
|
||||
g_action_change_state(rotate_action, g_variant_new_int32(active_rotate));
|
||||
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(flipped),
|
||||
head->transform == WL_OUTPUT_TRANSFORM_FLIPPED
|
||||
@ -645,36 +591,48 @@ void wd_ui_reset_heads(struct wd_state *state) {
|
||||
GtkWidget *mode_button = GTK_WIDGET(gtk_builder_get_object(builder, "mode_button"));
|
||||
GtkWidget *rotate_button = GTK_WIDGET(gtk_builder_get_object(builder, "rotate_button"));
|
||||
|
||||
GSimpleActionGroup *mode_actions = g_simple_action_group_new();
|
||||
gtk_widget_insert_action_group(mode_button, MODE_PREFIX, G_ACTION_GROUP(mode_actions));
|
||||
g_object_set_data(G_OBJECT(form), "mode-group", mode_actions);
|
||||
g_object_unref(mode_actions);
|
||||
GSimpleActionGroup *head_actions = g_simple_action_group_new();
|
||||
gtk_widget_insert_action_group(mode_button, HEAD_PREFIX, G_ACTION_GROUP(head_actions));
|
||||
gtk_widget_insert_action_group(rotate_button, HEAD_PREFIX, G_ACTION_GROUP(head_actions));
|
||||
|
||||
GSimpleActionGroup *transform_actions = g_simple_action_group_new();
|
||||
gtk_widget_insert_action_group(rotate_button, TRANSFORM_PREFIX, G_ACTION_GROUP(transform_actions));
|
||||
g_object_unref(transform_actions);
|
||||
GMenu *rotate_menu = g_menu_new();
|
||||
g_menu_append(rotate_menu, "Don't Rotate", "head.rotate(0)");
|
||||
g_menu_append(rotate_menu, "Rotate 90°", "head.rotate(90)");
|
||||
g_menu_append(rotate_menu, "Rotate 180°", "head.rotate(180)");
|
||||
g_menu_append(rotate_menu, "Rotate 270°", "head.rotate(270)");
|
||||
gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(rotate_button), G_MENU_MODEL(rotate_menu));
|
||||
|
||||
for (int i = 0; i < NUM_ROTATIONS; i++) {
|
||||
GtkWidget *button = GTK_WIDGET(gtk_builder_get_object(builder, ROTATE_IDS[i]));
|
||||
g_object_set(button, "role", GTK_BUTTON_ROLE_RADIO, NULL);
|
||||
GSimpleAction *action = g_simple_action_new(ROTATE_IDS[i], NULL);
|
||||
g_action_map_add_action(G_ACTION_MAP(transform_actions), G_ACTION(action));
|
||||
g_signal_connect(action, "activate", G_CALLBACK(rotate_selected), form);
|
||||
g_object_set_data(G_OBJECT(action), "widget", button);
|
||||
g_object_unref(action);
|
||||
}
|
||||
static const GVariantType * const mode_types[] = {
|
||||
G_VARIANT_TYPE_INT32,
|
||||
G_VARIANT_TYPE_INT32,
|
||||
G_VARIANT_TYPE_INT32
|
||||
};
|
||||
GSimpleAction *action = g_simple_action_new_stateful("mode",
|
||||
g_variant_type_new_tuple(mode_types, G_N_ELEMENTS(mode_types)),
|
||||
create_mode_variant(0, 0, 0));
|
||||
g_action_map_add_action(G_ACTION_MAP(head_actions), G_ACTION(action));
|
||||
g_signal_connect(action, "change-state", G_CALLBACK(mode_selected), form);
|
||||
g_object_set_data(G_OBJECT(form), "mode_action", action);
|
||||
g_object_unref(action);
|
||||
|
||||
action = g_simple_action_new_stateful(ROTATE_PREFIX, G_VARIANT_TYPE_INT32,
|
||||
g_variant_new_int32(0));
|
||||
g_action_map_add_action(G_ACTION_MAP(head_actions), G_ACTION(action));
|
||||
g_signal_connect(action, "change-state", G_CALLBACK(rotate_selected), form);
|
||||
g_object_set_data(G_OBJECT(form), "rotate_action", action);
|
||||
g_object_unref(action);
|
||||
|
||||
g_object_unref(head_actions);
|
||||
update_head_form(form, WD_FIELDS_ALL);
|
||||
|
||||
gtk_widget_show_all(form);
|
||||
|
||||
g_signal_connect_swapped(gtk_builder_get_object(builder, "enabled"), "toggled", G_CALLBACK(update_sensitivity), form);
|
||||
g_signal_connect(gtk_builder_get_object(builder, "width"), "value-changed", G_CALLBACK(mode_spin_changed), form);
|
||||
g_signal_connect(gtk_builder_get_object(builder, "height"), "value-changed", G_CALLBACK(mode_spin_changed), form);
|
||||
g_signal_connect(gtk_builder_get_object(builder, "refresh"), "value-changed", G_CALLBACK(mode_spin_changed), form);
|
||||
g_signal_connect_swapped(gtk_builder_get_object(builder, "enabled"), "toggled", G_CALLBACK(update_ui), state);
|
||||
g_signal_connect_swapped(gtk_builder_get_object(builder, "scale"), "value-changed", G_CALLBACK(update_ui), state);
|
||||
g_signal_connect_swapped(gtk_builder_get_object(builder, "pos_x"), "value-changed", G_CALLBACK(update_ui), state);
|
||||
g_signal_connect_swapped(gtk_builder_get_object(builder, "pos_y"), "value-changed", G_CALLBACK(update_ui), state);
|
||||
g_signal_connect_swapped(gtk_builder_get_object(builder, "width"), "value-changed", G_CALLBACK(update_ui), state);
|
||||
g_signal_connect_swapped(gtk_builder_get_object(builder, "height"), "value-changed", G_CALLBACK(update_ui), state);
|
||||
g_signal_connect_swapped(gtk_builder_get_object(builder, "refresh"), "value-changed", G_CALLBACK(update_ui), state);
|
||||
g_signal_connect_swapped(gtk_builder_get_object(builder, "flipped"), "toggled", G_CALLBACK(update_ui), state);
|
||||
|
||||
} else {
|
||||
@ -725,7 +683,7 @@ void wd_ui_reset_all(struct wd_state *state) {
|
||||
|
||||
void wd_ui_apply_done(struct wd_state *state, struct wl_list *outputs) {
|
||||
gtk_style_context_remove_class(gtk_widget_get_style_context(state->spinner), "visible");
|
||||
gtk_overlay_set_overlay_pass_through(GTK_OVERLAY(state->overlay), state->spinner, TRUE);
|
||||
gtk_widget_set_can_target(state->spinner, FALSE);
|
||||
gtk_spinner_stop(GTK_SPINNER(state->spinner));
|
||||
|
||||
gtk_widget_set_sensitive(state->stack_switcher, TRUE);
|
||||
@ -806,15 +764,18 @@ static void zoom_to(struct wd_state *state, double zoom) {
|
||||
update_zoom(state);
|
||||
}
|
||||
|
||||
static void zoom_out(struct wd_state *state) {
|
||||
static void zoom_out(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
zoom_to(state, state->zoom * 0.75);
|
||||
}
|
||||
|
||||
static void zoom_reset(struct wd_state *state) {
|
||||
static void zoom_reset(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
zoom_to(state, DEFAULT_ZOOM);
|
||||
}
|
||||
|
||||
static void zoom_in(struct wd_state *state) {
|
||||
static void zoom_in(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
zoom_to(state, state->zoom / 0.75);
|
||||
}
|
||||
|
||||
@ -943,82 +904,68 @@ static void set_clicked_head(struct wd_state *state,
|
||||
state->clicked = clicked;
|
||||
}
|
||||
|
||||
static gboolean canvas_click(GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data) {
|
||||
static void canvas_drag_begin(GtkGestureDrag *drag,
|
||||
gdouble mouse_x, gdouble mouse_y, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
if (event->button.type == GDK_BUTTON_PRESS) {
|
||||
if (event->button.button == 1) {
|
||||
guint button = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(drag));
|
||||
|
||||
if (button == 1) {
|
||||
struct wd_render_head_data *render;
|
||||
state->clicked = NULL;
|
||||
wl_list_for_each(render, &state->render.heads, link) {
|
||||
if (mouse_x >= render->x1 && mouse_x < render->x2 &&
|
||||
mouse_y >= render->y1 && mouse_y < render->y2) {
|
||||
set_clicked_head(state, render);
|
||||
state->click_offset.x = mouse_x - render->x1;
|
||||
state->click_offset.y = mouse_y - render->y1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state->clicked != NULL) {
|
||||
wl_list_remove(&state->clicked->link);
|
||||
wl_list_insert(&state->render.heads, &state->clicked->link);
|
||||
|
||||
struct wd_render_head_data *render;
|
||||
state->clicked = NULL;
|
||||
wl_list_for_each(render, &state->render.heads, link) {
|
||||
double mouse_x = event->button.x;
|
||||
double mouse_y = event->button.y;
|
||||
if (mouse_x >= render->x1 && mouse_x < render->x2 &&
|
||||
mouse_y >= render->y1 && mouse_y < render->y2) {
|
||||
set_clicked_head(state, render);
|
||||
state->click_offset.x = event->button.x - render->x1;
|
||||
state->click_offset.y = event->button.y - render->y1;
|
||||
render->updated_at = 0;
|
||||
render->preview = TRUE;
|
||||
}
|
||||
gtk_gl_area_queue_render(GTK_GL_AREA(state->canvas));
|
||||
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
|
||||
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
|
||||
const struct wd_head *other = g_object_get_data(G_OBJECT(form_iter->data), "head");
|
||||
if (state->clicked == other->render) {
|
||||
gtk_stack_set_visible_child(GTK_STACK(state->stack), form_iter->data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state->clicked != NULL) {
|
||||
wl_list_remove(&state->clicked->link);
|
||||
wl_list_insert(&state->render.heads, &state->clicked->link);
|
||||
|
||||
struct wd_render_head_data *render;
|
||||
wl_list_for_each(render, &state->render.heads, link) {
|
||||
render->updated_at = 0;
|
||||
render->preview = TRUE;
|
||||
}
|
||||
gtk_gl_area_queue_render(GTK_GL_AREA(state->canvas));
|
||||
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
|
||||
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
|
||||
const struct wd_head *other = g_object_get_data(G_OBJECT(form_iter->data), "head");
|
||||
if (state->clicked == other->render) {
|
||||
gtk_stack_set_visible_child(GTK_STACK(state->stack), form_iter->data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (event->button.button == 2) {
|
||||
state->panning = TRUE;
|
||||
state->pan_last.x = event->button.x;
|
||||
state->pan_last.y = event->button.y;
|
||||
}
|
||||
} else if (button == 2) {
|
||||
state->panning = TRUE;
|
||||
state->pan_last.x = mouse_x;
|
||||
state->pan_last.y = mouse_y;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean canvas_release(GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
if (event->button.button == 1) {
|
||||
set_clicked_head(state, NULL);
|
||||
}
|
||||
if (event->button.button == 2) {
|
||||
state->panning = FALSE;
|
||||
}
|
||||
update_cursor(state);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define SNAP_DIST 6.
|
||||
|
||||
static gboolean canvas_motion(GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data) {
|
||||
static void canvas_drag_update(GtkGestureDrag *drag,
|
||||
gdouble mouse_x, gdouble mouse_y, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
if (event->motion.state & GDK_BUTTON2_MASK) {
|
||||
guint button = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(drag));
|
||||
|
||||
if (button == 2) {
|
||||
GtkAdjustment *xadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(state->scroller));
|
||||
GtkAdjustment *yadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(state->scroller));
|
||||
double delta_x = event->motion.x - state->pan_last.x;
|
||||
double delta_y = event->motion.y - state->pan_last.y;
|
||||
double delta_x = mouse_x - state->pan_last.x;
|
||||
double delta_y = mouse_y - state->pan_last.y;
|
||||
gtk_adjustment_set_value(xadj, gtk_adjustment_get_value(xadj) + delta_x);
|
||||
gtk_adjustment_set_value(yadj, gtk_adjustment_get_value(yadj) + delta_y);
|
||||
state->pan_last.x = event->motion.x;
|
||||
state->pan_last.y = event->motion.y;
|
||||
state->pan_last.x = mouse_x;
|
||||
state->pan_last.y = mouse_y;
|
||||
queue_canvas_draw(state);
|
||||
}
|
||||
if ((event->motion.state & GDK_BUTTON1_MASK) && state->clicked != NULL) {
|
||||
if (button == 1 && state->clicked != NULL) {
|
||||
GtkWidget *form = NULL;
|
||||
g_autoptr(GList) forms = gtk_container_get_children(GTK_CONTAINER(state->stack));
|
||||
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
|
||||
@ -1044,9 +991,9 @@ static gboolean canvas_motion(GtkWidget *widget, GdkEvent *event,
|
||||
SWAP(int, size.x, size.y);
|
||||
}
|
||||
struct wd_point tl = {
|
||||
.x = (event->motion.x - state->click_offset.x
|
||||
.x = (mouse_x - state->click_offset.x
|
||||
+ state->render.x_origin + state->render.scroll_x) / state->zoom,
|
||||
.y = (event->motion.y - state->click_offset.y
|
||||
.y = (mouse_y - state->click_offset.y
|
||||
+ state->render.y_origin + state->render.scroll_y) / state->zoom
|
||||
};
|
||||
const struct wd_point br = {
|
||||
@ -1056,9 +1003,12 @@ static gboolean canvas_motion(GtkWidget *widget, GdkEvent *event,
|
||||
struct wd_point new_pos = tl;
|
||||
float snap = SNAP_DIST / state->zoom;
|
||||
|
||||
GdkEvent *event = gtk_get_current_event();
|
||||
GdkModifierType mod_state = gdk_event_get_modifier_state(event);
|
||||
|
||||
for (GList *form_iter = forms; form_iter != NULL; form_iter = form_iter->next) {
|
||||
const struct wd_head *other = g_object_get_data(G_OBJECT(form_iter->data), "head");
|
||||
if (other->render != state->clicked && !(event->motion.state & GDK_SHIFT_MASK)) {
|
||||
if (other->render != state->clicked && !(mod_state & GDK_SHIFT_MASK)) {
|
||||
GtkBuilder *other_builder = GTK_BUILDER(g_object_get_data(G_OBJECT(form_iter->data), "builder"));
|
||||
double x1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "pos_x")));
|
||||
double y1 = gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtk_builder_get_object(other_builder, "pos_y")));
|
||||
@ -1108,96 +1058,87 @@ static gboolean canvas_motion(GtkWidget *widget, GdkEvent *event,
|
||||
gtk_spin_button_set_value(GTK_SPIN_BUTTON(pos_y), new_pos.y);
|
||||
}
|
||||
}
|
||||
update_hovered(state);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean canvas_enter(GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data) {
|
||||
static void canvas_drag_end(GtkGestureDrag *drag,
|
||||
gdouble mouse_x, gdouble mouse_y, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
if (!(event->crossing.state & GDK_BUTTON1_MASK)) {
|
||||
guint button = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(drag));
|
||||
if (button == 1) {
|
||||
set_clicked_head(state, NULL);
|
||||
}
|
||||
if (!(event->crossing.state & GDK_BUTTON2_MASK)) {
|
||||
if (button == 2) {
|
||||
state->panning = FALSE;
|
||||
}
|
||||
update_cursor(state);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean canvas_leave(GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data) {
|
||||
static void canvas_motion(GtkEventControllerMotion *controller,
|
||||
gdouble mouse_x, gdouble mouse_y, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
update_hovered(state, mouse_x, mouse_y);
|
||||
}
|
||||
|
||||
static void canvas_enter(GtkEventControllerMotion *controller,
|
||||
gdouble x, gdouble y, GdkCrossingMode crossing_mode, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
GdkEvent *event = gtk_get_current_event();
|
||||
GdkModifierType mod_state = gdk_event_get_modifier_state(event);
|
||||
|
||||
if (!(mod_state & GDK_BUTTON1_MASK)) {
|
||||
set_clicked_head(state, NULL);
|
||||
}
|
||||
if (!(mod_state & GDK_BUTTON2_MASK)) {
|
||||
state->panning = FALSE;
|
||||
}
|
||||
update_cursor(state);
|
||||
}
|
||||
|
||||
static void canvas_leave(GtkEventControllerMotion *controller,
|
||||
GdkCrossingMode crossing_mode, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
struct wd_render_head_data *render;
|
||||
wl_list_for_each(render, &state->render.heads, link) {
|
||||
render->hovered = FALSE;
|
||||
}
|
||||
update_tick_callback(state);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean canvas_scroll(GtkWidget *widget, GdkEvent *event,
|
||||
gpointer data) {
|
||||
static gboolean canvas_scroll(GtkEventControllerScroll *controller,
|
||||
gdouble delta_x, gdouble delta_y, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
if (event->scroll.state & GDK_CONTROL_MASK) {
|
||||
switch (event->scroll.direction) {
|
||||
case GDK_SCROLL_UP:
|
||||
zoom_in(state);
|
||||
break;
|
||||
case GDK_SCROLL_DOWN:
|
||||
zoom_out(state);
|
||||
break;
|
||||
case GDK_SCROLL_SMOOTH:
|
||||
if (event->scroll.delta_y)
|
||||
zoom_to(state, state->zoom * pow(0.75, event->scroll.delta_y));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
GdkEvent *event = gtk_get_current_event();
|
||||
GdkModifierType mod_state = gdk_event_get_modifier_state(event);
|
||||
|
||||
if (mod_state & GDK_CONTROL_MASK) {
|
||||
if (delta_y)
|
||||
zoom_to(state, state->zoom * pow(0.75, delta_y));
|
||||
} else {
|
||||
GtkAdjustment *xadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(state->scroller));
|
||||
GtkAdjustment *yadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(state->scroller));
|
||||
double xstep = gtk_adjustment_get_step_increment(xadj);
|
||||
double ystep = gtk_adjustment_get_step_increment(yadj);
|
||||
switch (event->scroll.direction) {
|
||||
case GDK_SCROLL_UP:
|
||||
gtk_adjustment_set_value(yadj, gtk_adjustment_get_value(yadj) - ystep);
|
||||
break;
|
||||
case GDK_SCROLL_DOWN:
|
||||
gtk_adjustment_set_value(yadj, gtk_adjustment_get_value(yadj) + ystep);
|
||||
break;
|
||||
case GDK_SCROLL_LEFT:
|
||||
gtk_adjustment_set_value(xadj, gtk_adjustment_get_value(xadj) - xstep);
|
||||
break;
|
||||
case GDK_SCROLL_RIGHT:
|
||||
gtk_adjustment_set_value(xadj, gtk_adjustment_get_value(xadj) + xstep);
|
||||
break;
|
||||
case GDK_SCROLL_SMOOTH:
|
||||
if (event->scroll.delta_x)
|
||||
gtk_adjustment_set_value(xadj, gtk_adjustment_get_value(xadj) + xstep * event->scroll.delta_x);
|
||||
if (event->scroll.delta_y)
|
||||
gtk_adjustment_set_value(yadj, gtk_adjustment_get_value(yadj) + ystep * event->scroll.delta_y);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (delta_x)
|
||||
gtk_adjustment_set_value(xadj, gtk_adjustment_get_value(xadj) + xstep * delta_x);
|
||||
if (delta_y)
|
||||
gtk_adjustment_set_value(yadj, gtk_adjustment_get_value(yadj) + ystep * delta_y);
|
||||
}
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void canvas_resize(GtkWidget *widget, GdkRectangle *allocation,
|
||||
gpointer data) {
|
||||
static void canvas_resize(GtkWidget *widget, gint width, gint height,
|
||||
gint baseline, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
update_scroll_size(state);
|
||||
}
|
||||
|
||||
static void cancel_changes(GtkButton *button, gpointer data) {
|
||||
static void cancel_changes(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
gtk_stack_set_visible_child_name(GTK_STACK(state->header_stack), "title");
|
||||
wd_ui_reset_all(state);
|
||||
}
|
||||
|
||||
static void apply_changes(GtkButton *button, gpointer data) {
|
||||
static void apply_changes(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
apply_state(data);
|
||||
}
|
||||
|
||||
@ -1215,8 +1156,8 @@ static void info_bar_animation_done(GObject *object, GParamSpec *pspec, gpointer
|
||||
|
||||
static void auto_apply_selected(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
state->autoapply = !state->autoapply;
|
||||
g_simple_action_set_state(action, g_variant_new_boolean(state->autoapply));
|
||||
state->autoapply = g_variant_get_boolean(param);
|
||||
g_simple_action_set_state(action, param);
|
||||
}
|
||||
|
||||
static gboolean redraw_canvas(GtkWidget *widget, GdkFrameClock *frame_clock, gpointer data) {
|
||||
@ -1231,15 +1172,15 @@ static gboolean redraw_canvas(GtkWidget *widget, GdkFrameClock *frame_clock, gpo
|
||||
|
||||
static void capture_selected(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
state->capture = !state->capture;
|
||||
g_simple_action_set_state(action, g_variant_new_boolean(state->capture));
|
||||
state->capture = g_variant_get_boolean(param);
|
||||
g_simple_action_set_state(action, param);
|
||||
update_tick_callback(state);
|
||||
}
|
||||
|
||||
static void overlay_selected(GSimpleAction *action, GVariant *param, gpointer data) {
|
||||
struct wd_state *state = data;
|
||||
state->show_overlay = !state->show_overlay;
|
||||
g_simple_action_set_state(action, g_variant_new_boolean(state->show_overlay));
|
||||
state->show_overlay = g_variant_get_boolean(param);
|
||||
g_simple_action_set_state(action, param);
|
||||
|
||||
struct wd_output *output;
|
||||
wl_list_for_each(output, &state->outputs, link) {
|
||||
@ -1265,15 +1206,18 @@ static void activate(GtkApplication* app, gpointer user_data) {
|
||||
|
||||
GtkCssProvider *css_provider = gtk_css_provider_new();
|
||||
gtk_css_provider_load_from_resource(css_provider, "/style.css");
|
||||
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(css_provider),
|
||||
gtk_style_context_add_provider_for_display(gdk_display, GTK_STYLE_PROVIDER(css_provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
|
||||
state->grab_cursor = gdk_cursor_new_from_name(gdk_display, "grab");
|
||||
state->grabbing_cursor = gdk_cursor_new_from_name(gdk_display, "grabbing");
|
||||
state->move_cursor = gdk_cursor_new_from_name(gdk_display, "move");
|
||||
GdkCursor *default_cursor = gdk_cursor_new_from_name("default", NULL);
|
||||
state->grab_cursor = gdk_cursor_new_from_name("grab", default_cursor);
|
||||
state->grabbing_cursor = gdk_cursor_new_from_name("grabbing", default_cursor);
|
||||
state->move_cursor = gdk_cursor_new_from_name("move", default_cursor);
|
||||
g_object_unref(default_cursor);
|
||||
|
||||
GtkBuilder *builder = gtk_builder_new_from_resource("/wdisplays.ui");
|
||||
GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "heads_window"));
|
||||
GtkWidget *paned = GTK_WIDGET(gtk_builder_get_object(builder, "paned"));
|
||||
state->header_stack = GTK_WIDGET(gtk_builder_get_object(builder, "header_stack"));
|
||||
state->stack_switcher = GTK_WIDGET(gtk_builder_get_object(builder, "heads_stack_switcher"));
|
||||
state->stack = GTK_WIDGET(gtk_builder_get_object(builder, "heads_stack"));
|
||||
@ -1287,35 +1231,37 @@ static void activate(GtkApplication* app, gpointer user_data) {
|
||||
state->info_label = GTK_WIDGET(gtk_builder_get_object(builder, "heads_info_label"));
|
||||
state->menu_button = GTK_WIDGET(gtk_builder_get_object(builder, "menu_button"));
|
||||
|
||||
gtk_builder_add_callback_symbol(builder, "apply_changes", G_CALLBACK(apply_changes));
|
||||
gtk_builder_add_callback_symbol(builder, "cancel_changes", G_CALLBACK(cancel_changes));
|
||||
gtk_builder_add_callback_symbol(builder, "zoom_out", G_CALLBACK(zoom_out));
|
||||
gtk_builder_add_callback_symbol(builder, "zoom_reset", G_CALLBACK(zoom_reset));
|
||||
gtk_builder_add_callback_symbol(builder, "zoom_in", G_CALLBACK(zoom_in));
|
||||
gtk_builder_add_callback_symbol(builder, "info_response", G_CALLBACK(info_response));
|
||||
gtk_builder_add_callback_symbol(builder, "destroy", G_CALLBACK(cleanup));
|
||||
gtk_builder_connect_signals(builder, state);
|
||||
gtk_box_set_homogeneous(GTK_BOX(gtk_builder_get_object(builder, "zoom_box")), FALSE);
|
||||
g_signal_connect(window, "destroy", G_CALLBACK(cleanup), state);
|
||||
|
||||
g_object_set(paned, "shrink-child1", FALSE, "shrink-child2", FALSE, NULL);
|
||||
|
||||
state->canvas = wd_gl_viewport_new();
|
||||
gtk_container_add(GTK_CONTAINER(state->scroller), state->canvas);
|
||||
gtk_widget_add_events(state->canvas, GDK_POINTER_MOTION_MASK
|
||||
| GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK
|
||||
| GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
|
||||
gtk_widget_set_hexpand(state->canvas, TRUE);
|
||||
gtk_widget_set_vexpand(state->canvas, TRUE);
|
||||
g_signal_connect(state->canvas, "realize", G_CALLBACK(canvas_realize), state);
|
||||
g_signal_connect(state->canvas, "render", G_CALLBACK(canvas_render), state);
|
||||
g_signal_connect(state->canvas, "unrealize", G_CALLBACK(canvas_unrealize), state);
|
||||
g_signal_connect(state->canvas, "button-press-event", G_CALLBACK(canvas_click), state);
|
||||
g_signal_connect(state->canvas, "button-release-event", G_CALLBACK(canvas_release), state);
|
||||
g_signal_connect(state->canvas, "enter-notify-event", G_CALLBACK(canvas_enter), state);
|
||||
g_signal_connect(state->canvas, "leave-notify-event", G_CALLBACK(canvas_leave), state);
|
||||
g_signal_connect(state->canvas, "motion-notify-event", G_CALLBACK(canvas_motion), state);
|
||||
g_signal_connect(state->canvas, "scroll-event", G_CALLBACK(canvas_scroll), state);
|
||||
g_signal_connect(state->canvas, "size-allocate", G_CALLBACK(canvas_resize), state);
|
||||
gtk_gl_area_set_required_version(GTK_GL_AREA(state->canvas), 2, 0);
|
||||
gtk_gl_area_set_use_es(GTK_GL_AREA(state->canvas), TRUE);
|
||||
gtk_gl_area_set_has_alpha(GTK_GL_AREA(state->canvas), TRUE);
|
||||
gtk_gl_area_set_auto_render(GTK_GL_AREA(state->canvas), state->capture);
|
||||
|
||||
GtkGesture *canvas_drag_controller = gtk_gesture_drag_new();
|
||||
gtk_widget_add_controller(state->canvas, GTK_EVENT_CONTROLLER(canvas_drag_controller));
|
||||
GtkEventController *canvas_motion_controller = gtk_event_controller_motion_new();
|
||||
gtk_widget_add_controller(state->canvas, canvas_motion_controller);
|
||||
GtkEventController *canvas_scroll_controller = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES);
|
||||
gtk_widget_add_controller(state->canvas, canvas_scroll_controller);
|
||||
g_signal_connect(canvas_drag_controller, "drag-begin", G_CALLBACK(canvas_drag_begin), state);
|
||||
g_signal_connect(canvas_drag_controller, "drag-update", G_CALLBACK(canvas_drag_update), state);
|
||||
g_signal_connect(canvas_drag_controller, "drag-end", G_CALLBACK(canvas_drag_end), state);
|
||||
g_signal_connect(canvas_motion_controller, "enter", G_CALLBACK(canvas_enter), state);
|
||||
g_signal_connect(canvas_motion_controller, "leave", G_CALLBACK(canvas_leave), state);
|
||||
g_signal_connect(canvas_motion_controller, "motion", G_CALLBACK(canvas_motion), state);
|
||||
g_signal_connect(canvas_scroll_controller, "scroll", G_CALLBACK(canvas_scroll), state);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(state->scroller), state->canvas);
|
||||
|
||||
GtkAdjustment *scroll_x_adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(state->scroller));
|
||||
GtkAdjustment *scroll_y_adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(state->scroller));
|
||||
g_signal_connect_swapped(scroll_x_adj, "value-changed", G_CALLBACK(queue_canvas_draw), state);
|
||||
@ -1324,24 +1270,51 @@ static void activate(GtkApplication* app, gpointer user_data) {
|
||||
update_zoom(state);
|
||||
|
||||
GSimpleActionGroup *main_actions = g_simple_action_group_new();
|
||||
gtk_widget_insert_action_group(state->menu_button, APP_PREFIX, G_ACTION_GROUP(main_actions));
|
||||
gtk_widget_insert_action_group(window, APP_PREFIX, G_ACTION_GROUP(main_actions));
|
||||
g_object_unref(main_actions);
|
||||
|
||||
GSimpleAction *autoapply_action = g_simple_action_new_stateful("auto-apply", NULL,
|
||||
GSimpleAction *action = g_simple_action_new("apply-changes", NULL);
|
||||
g_signal_connect(action, "activate", G_CALLBACK(apply_changes), state);
|
||||
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
|
||||
|
||||
action = g_simple_action_new("cancel-changes", NULL);
|
||||
g_signal_connect(action, "activate", G_CALLBACK(cancel_changes), state);
|
||||
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
|
||||
|
||||
action = g_simple_action_new("zoom-out", NULL);
|
||||
g_signal_connect(action, "activate", G_CALLBACK(zoom_out), state);
|
||||
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
|
||||
|
||||
action = g_simple_action_new("zoom-reset", NULL);
|
||||
g_signal_connect(action, "activate", G_CALLBACK(zoom_reset), state);
|
||||
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
|
||||
|
||||
action = g_simple_action_new("zoom-in", NULL);
|
||||
g_signal_connect(action, "activate", G_CALLBACK(zoom_in), state);
|
||||
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
|
||||
|
||||
action = g_simple_action_new_stateful("auto-apply", NULL,
|
||||
g_variant_new_boolean(state->autoapply));
|
||||
g_signal_connect(autoapply_action, "activate", G_CALLBACK(auto_apply_selected), state);
|
||||
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(autoapply_action));
|
||||
g_signal_connect(action, "change-state", G_CALLBACK(auto_apply_selected), state);
|
||||
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(action));
|
||||
|
||||
GSimpleAction *capture_action = g_simple_action_new_stateful("capture-screens", NULL,
|
||||
g_variant_new_boolean(state->capture));
|
||||
g_signal_connect(capture_action, "activate", G_CALLBACK(capture_selected), state);
|
||||
g_signal_connect(capture_action, "change-state", G_CALLBACK(capture_selected), state);
|
||||
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(capture_action));
|
||||
|
||||
GSimpleAction *overlay_action = g_simple_action_new_stateful("show-overlay", NULL,
|
||||
g_variant_new_boolean(state->show_overlay));
|
||||
g_signal_connect(overlay_action, "activate", G_CALLBACK(overlay_selected), state);
|
||||
g_signal_connect(overlay_action, "change-state", G_CALLBACK(overlay_selected), state);
|
||||
g_action_map_add_action(G_ACTION_MAP(main_actions), G_ACTION(overlay_action));
|
||||
|
||||
GMenu *main_menu = g_menu_new();
|
||||
g_menu_append(main_menu, "_Automatically Apply Changes", "app.auto-apply");
|
||||
g_menu_append(main_menu, "_Show Screen Contents", "app.capture-screens");
|
||||
g_menu_append(main_menu, "_Overlay Screen Names", "app.show-overlay");
|
||||
gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(state->menu_button), G_MENU_MODEL(main_menu));
|
||||
|
||||
g_signal_connect(state->info_bar, "response", G_CALLBACK(info_response), state);
|
||||
/* first child of GtkInfoBar is always GtkRevealer */
|
||||
g_autoptr(GList) info_children = gtk_container_get_children(GTK_CONTAINER(state->info_bar));
|
||||
g_signal_connect(info_children->data, "notify::child-revealed", G_CALLBACK(info_bar_animation_done), state);
|
||||
@ -1376,13 +1349,15 @@ static void activate(GtkApplication* app, gpointer user_data) {
|
||||
g_signal_connect(gdk_display, "monitor-removed", G_CALLBACK(monitor_removed), state);
|
||||
|
||||
gtk_application_add_window(app, GTK_WINDOW(window));
|
||||
gtk_widget_show_all(window);
|
||||
g_object_unref(builder);
|
||||
update_tick_callback(state);
|
||||
|
||||
gtk_window_present(GTK_WINDOW(window));
|
||||
}
|
||||
// END GLOBAL CALLBACKS
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
GtkApplication *app = gtk_application_new("org.swaywm.sway-outputs", G_APPLICATION_FLAGS_NONE);
|
||||
GtkApplication *app = gtk_application_new(WDISPLAYS_APP_ID, G_APPLICATION_FLAGS_NONE);
|
||||
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
|
||||
int status = g_application_run(G_APPLICATION(app), argc, argv);
|
||||
g_object_unref(app);
|
||||
|
@ -2,36 +2,22 @@
|
||||
cc = meson.get_compiler('c')
|
||||
m_dep = cc.find_library('m', required : false)
|
||||
rt_dep = cc.find_library('rt', required : false)
|
||||
gdk = dependency('gdk-3.0')
|
||||
gtk = dependency('gtk+-3.0')
|
||||
assert(gdk.get_pkgconfig_variable('targets').split().contains('wayland'), 'Wayland GDK backend not present')
|
||||
gtk = dependency('gtk4')
|
||||
assert(gtk.get_pkgconfig_variable('targets').split().contains('wayland'), 'Wayland GDK backend not present')
|
||||
epoxy = dependency('epoxy')
|
||||
gmodule_export = dependency('gmodule-export-2.0')
|
||||
|
||||
wdisplays_src = files(
|
||||
'main.c',
|
||||
'outputs.c',
|
||||
'render.c',
|
||||
'glviewport.c',
|
||||
'overlay.c'
|
||||
)
|
||||
wdisplays_args = []
|
||||
|
||||
kanshictl = find_program('kanshictl', required: get_option('kanshi'))
|
||||
if kanshictl.found()
|
||||
wdisplays_src += files(
|
||||
'parser.c'
|
||||
)
|
||||
wdisplays_args += [
|
||||
'-DHAVE_KANSHI',
|
||||
'-DKANSHICTL_PATH="@0@"'.format(kanshictl.path())
|
||||
]
|
||||
endif
|
||||
configure_file(input: 'config.h.in', output: 'config.h', configuration: conf)
|
||||
|
||||
executable(
|
||||
'wdisplays',
|
||||
[
|
||||
wdisplays_src,
|
||||
resources
|
||||
'main.c',
|
||||
'outputs.c',
|
||||
'render.c',
|
||||
'glviewport.c',
|
||||
#'overlay.c',
|
||||
resources,
|
||||
],
|
||||
dependencies : [
|
||||
m_dep,
|
||||
@ -39,8 +25,9 @@ executable(
|
||||
wayland_client,
|
||||
client_protos,
|
||||
epoxy,
|
||||
gtk
|
||||
gtk,
|
||||
gmodule_export
|
||||
],
|
||||
c_args: wdisplays_args,
|
||||
c_args: ['-DGDK_DISABLE_DEPRECATED', '-DGTK_DISABLE_DEPRECATED'],
|
||||
install: true
|
||||
)
|
||||
|
158
src/outputs.c
158
src/outputs.c
@ -1,32 +1,13 @@
|
||||
/*
|
||||
* Copyright (C) 2019 cyclopsian
|
||||
* Copyright (C) 2017-2019 emersion
|
||||
|
||||
* 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 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 X CONSORTIUM 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.
|
||||
*/
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright (C) 2019 cyclopsian
|
||||
* Copyright (C) 2017-2019 emersion */
|
||||
|
||||
/*
|
||||
* Parts of this file are taken from emersion/kanshi:
|
||||
* https://github.com/emersion/kanshi/blob/38d27474b686fcc8324cc5e454741a49577c0988/main.c
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -39,12 +20,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "wdisplays.h"
|
||||
#ifdef HAVE_KANSHI
|
||||
#include "kanshi.h"
|
||||
#endif
|
||||
|
||||
#include "wlr-output-management-unstable-v1-client-protocol.h"
|
||||
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||
@ -70,109 +46,6 @@ static void destroy_pending(struct wd_pending_config *pending) {
|
||||
free(pending);
|
||||
}
|
||||
|
||||
#ifdef HAVE_KANSHI
|
||||
static struct kanshi_profile *find_active_profile(
|
||||
const struct kanshi_config *config, const struct wl_list *outputs) {
|
||||
struct kanshi_profile *profile;
|
||||
int head_count = wl_list_length(outputs);
|
||||
wl_list_for_each(profile, &config->profiles, link) {
|
||||
unsigned found_count = 0;
|
||||
struct kanshi_profile_output *output;
|
||||
wl_list_for_each(output, &profile->outputs, link) {
|
||||
struct wd_head_config *head;
|
||||
wl_list_for_each(head, outputs, link) {
|
||||
if (strcmp(output->name, head->head->name) == 0) {
|
||||
found_count++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found_count == head_count) {
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void update_profile_output(struct kanshi_profile_output *output,
|
||||
const struct wd_head_config *head) {
|
||||
output->enabled = head->enabled;
|
||||
output->fields |= KANSHI_OUTPUT_ENABLED;
|
||||
if (head->enabled) {
|
||||
output->fields |= KANSHI_OUTPUT_MODE | KANSHI_OUTPUT_POSITION
|
||||
| KANSHI_OUTPUT_SCALE | KANSHI_OUTPUT_TRANSFORM;
|
||||
output->mode.width = head->width;
|
||||
output->mode.height = head->height;
|
||||
output->mode.refresh = head->refresh;
|
||||
output->position.x = head->x;
|
||||
output->position.y = head->y;
|
||||
output->scale = head->scale;
|
||||
output->transform = head->transform;
|
||||
}
|
||||
}
|
||||
|
||||
static bool save_config(const struct wl_list *outputs) {
|
||||
g_autofree const char *config_dir = g_strjoin("/",
|
||||
g_get_user_config_dir(), "kanshi/config.d", NULL);
|
||||
if (g_mkdir_with_parents(config_dir, 0700) == -1) {
|
||||
fprintf(stderr, "g_mkdir_with_parents failed: %s: %s\n",
|
||||
config_dir, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
g_autofree const char *config_path = g_strjoin("/",
|
||||
config_dir, "50-wdisplays", NULL);
|
||||
struct kanshi_config *config = kanshi_parse_config(config_path);
|
||||
if (config == NULL) {
|
||||
config = calloc(1, sizeof(*config));
|
||||
wl_list_init(&config->profiles);
|
||||
}
|
||||
struct kanshi_profile *profile = find_active_profile(config, outputs);
|
||||
if (profile == NULL) {
|
||||
profile = calloc(1, sizeof(*profile));
|
||||
wl_list_init(&profile->outputs);
|
||||
struct wd_head_config *head;
|
||||
wl_list_for_each(head, outputs, link) {
|
||||
struct kanshi_profile_output *output = calloc(1, sizeof(*output));
|
||||
output->name = strdup(head->head->name);
|
||||
update_profile_output(output, head);
|
||||
wl_list_insert(profile->outputs.prev, &output->link);
|
||||
}
|
||||
wl_list_insert(&config->profiles, &profile->link);
|
||||
} else {
|
||||
struct kanshi_profile_output *output;
|
||||
wl_list_for_each(output, &profile->outputs, link) {
|
||||
struct wd_head_config *head;
|
||||
wl_list_for_each(head, outputs, link) {
|
||||
if (strcmp(output->name, head->head->name) == 0) {
|
||||
update_profile_output(output, head);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kanshi_save_config(config_path, config);
|
||||
kanshi_destroy_config(config);
|
||||
|
||||
GError *error = NULL;
|
||||
gchar *argv[] = { KANSHICTL_PATH, "reload", NULL };
|
||||
gint status;
|
||||
g_spawn_sync(NULL, argv, NULL, G_SPAWN_DEFAULT, NULL, NULL,
|
||||
NULL, NULL, &status, &error);
|
||||
if (error != NULL) {
|
||||
fprintf(stderr, "Could not execute " KANSHICTL_PATH ": %s\n",
|
||||
error->message);
|
||||
g_error_free(error);
|
||||
return false;
|
||||
}
|
||||
if (status != EXIT_SUCCESS) {
|
||||
fprintf(stderr, "Could not execute " KANSHICTL_PATH "\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void config_handle_succeeded(void *data,
|
||||
struct zwlr_output_configuration_v1 *config) {
|
||||
struct wd_pending_config *pending = data;
|
||||
@ -210,21 +83,13 @@ static const struct zwlr_output_configuration_v1_listener config_listener = {
|
||||
|
||||
void wd_apply_state(struct wd_state *state, struct wl_list *new_outputs,
|
||||
struct wl_display *display) {
|
||||
struct zwlr_output_configuration_v1 *config =
|
||||
zwlr_output_manager_v1_create_configuration(state->output_manager, state->serial);
|
||||
|
||||
struct wd_pending_config *pending = calloc(1, sizeof(*pending));
|
||||
pending->state = state;
|
||||
pending->outputs = new_outputs;
|
||||
|
||||
#ifdef HAVE_KANSHI
|
||||
if (save_config(new_outputs)) {
|
||||
wd_ui_apply_done(pending->state, pending->outputs);
|
||||
destroy_pending(pending);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct zwlr_output_configuration_v1 *config =
|
||||
zwlr_output_manager_v1_create_configuration(state->output_manager, state->serial);
|
||||
|
||||
zwlr_output_configuration_v1_add_listener(config, &config_listener, pending);
|
||||
|
||||
ssize_t i = -1;
|
||||
@ -292,13 +157,20 @@ static void wd_frame_destroy(struct wd_frame *frame) {
|
||||
}
|
||||
|
||||
static int create_shm_file(size_t size, const char *fmt, ...) {
|
||||
char *shm_name = NULL;
|
||||
int fd = -1;
|
||||
|
||||
va_list vl;
|
||||
va_start(vl, fmt);
|
||||
char *shm_name = g_strdup_vprintf(fmt, vl);
|
||||
int result = vasprintf(&shm_name, fmt, vl);
|
||||
va_end(vl);
|
||||
|
||||
if (result == -1) {
|
||||
fprintf(stderr, "asprintf: %s\n", strerror(errno));
|
||||
shm_name = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = shm_open(shm_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "shm_open: %s\n", strerror(errno));
|
||||
|
@ -1,25 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 cyclopsian
|
||||
|
||||
* 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 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 X CONSORTIUM 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.
|
||||
*/
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright (C) 2019 cyclopsian */
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
@ -27,7 +7,7 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkwayland.h>
|
||||
#include <gdk/wayland/gdkwayland.h>
|
||||
|
||||
#include "wdisplays.h"
|
||||
|
||||
@ -59,8 +39,8 @@ static inline int min(int a, int b) {
|
||||
static PangoLayout *create_text_layout(struct wd_head *head,
|
||||
PangoContext *pango, GtkStyleContext *style) {
|
||||
GtkStyleContext *desc_style = gtk_style_context_new();
|
||||
gtk_style_context_set_screen(desc_style,
|
||||
gtk_style_context_get_screen(style));
|
||||
gtk_style_context_set_display(desc_style,
|
||||
gtk_style_context_get_display(style));
|
||||
GtkWidgetPath *desc_path = gtk_widget_path_copy(
|
||||
gtk_style_context_get_path(style));
|
||||
gtk_widget_path_append_type(desc_path, G_TYPE_NONE);
|
||||
@ -68,8 +48,7 @@ static PangoLayout *create_text_layout(struct wd_head *head,
|
||||
gtk_style_context_add_class(desc_style, "description");
|
||||
|
||||
double desc_font_size = 16.;
|
||||
gtk_style_context_get(desc_style, GTK_STATE_FLAG_NORMAL,
|
||||
"font-size", &desc_font_size, NULL);
|
||||
gtk_style_context_get(desc_style, "font-size", &desc_font_size, NULL);
|
||||
|
||||
g_autofree gchar *str = g_strdup_printf("%s\n<span size=\"%d\">%s</span>",
|
||||
head->name, (int) (desc_font_size * PANGO_SCALE), head->description);
|
||||
@ -90,7 +69,7 @@ static void resize(struct wd_output *output) {
|
||||
}
|
||||
uint32_t margin = min(screen_width, screen_height) * SCREEN_MARGIN_PERCENT;
|
||||
|
||||
GdkWindow *window = gtk_widget_get_window(output->overlay_window);
|
||||
GdkSurface *surface = gtk_widget_get_surface(output->overlay_window);
|
||||
PangoContext *pango = gtk_widget_get_pango_context(output->overlay_window);
|
||||
GtkStyleContext *style_ctx = gtk_widget_get_style_context(
|
||||
output->overlay_window);
|
||||
@ -103,7 +82,7 @@ static void resize(struct wd_output *output) {
|
||||
|
||||
|
||||
GtkBorder padding;
|
||||
gtk_style_context_get_padding(style_ctx, GTK_STATE_FLAG_NORMAL, &padding);
|
||||
gtk_style_context_get_padding(style_ctx, &padding);
|
||||
|
||||
width = min(width, screen_width - margin * 2)
|
||||
+ padding.left + padding.right;
|
||||
@ -115,10 +94,10 @@ static void resize(struct wd_output *output) {
|
||||
zwlr_layer_surface_v1_set_size(output->overlay_layer_surface,
|
||||
width, height);
|
||||
|
||||
struct wl_surface *surface = gdk_wayland_window_get_wl_surface(window);
|
||||
wl_surface_commit(surface);
|
||||
struct wl_surface *wl_surface = gdk_wayland_surface_get_wl_surface(surface);
|
||||
wl_surface_commit(wl_surface);
|
||||
|
||||
GdkDisplay *display = gdk_window_get_display(window);
|
||||
GdkDisplay *display = gdk_surface_get_display(surface);
|
||||
wl_display_roundtrip(gdk_wayland_display_get_wl_display(display));
|
||||
}
|
||||
|
||||
@ -130,22 +109,23 @@ void wd_redraw_overlay(struct wd_output *output) {
|
||||
}
|
||||
|
||||
void window_realize(GtkWidget *widget, gpointer data) {
|
||||
GdkWindow *window = gtk_widget_get_window(widget);
|
||||
gdk_wayland_window_set_use_custom_surface(window);
|
||||
//FIXME - custom surfaces in GTK4 wayland?
|
||||
GdkSurface *surface = gtk_widget_get_surface(widget);
|
||||
gdk_wayland_surface_set_use_custom_surface(surface);
|
||||
}
|
||||
|
||||
void window_map(GtkWidget *widget, gpointer data) {
|
||||
struct wd_output *output = data;
|
||||
|
||||
GdkWindow *window = gtk_widget_get_window(widget);
|
||||
GdkSurface *surface = gtk_widget_get_surface(widget);
|
||||
cairo_region_t *region = cairo_region_create();
|
||||
gdk_window_input_shape_combine_region(window, region, 0, 0);
|
||||
gdk_surface_input_shape_combine_region(surface, region, 0, 0);
|
||||
cairo_region_destroy(region);
|
||||
|
||||
struct wl_surface *surface = gdk_wayland_window_get_wl_surface(window);
|
||||
struct wl_surface *wl_surface = gdk_wayland_surface_get_wl_surface(surface);
|
||||
|
||||
output->overlay_layer_surface = zwlr_layer_shell_v1_get_layer_surface(
|
||||
output->state->layer_shell, surface, output->wl_output,
|
||||
output->state->layer_shell, wl_surface, output->wl_output,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_TOP, "output-overlay");
|
||||
|
||||
zwlr_layer_surface_v1_add_listener(output->overlay_layer_surface,
|
||||
@ -169,14 +149,14 @@ gboolean window_draw(GtkWidget *widget, cairo_t *cr, gpointer data) {
|
||||
|
||||
GtkStyleContext *style_ctx = gtk_widget_get_style_context(widget);
|
||||
GdkRGBA fg;
|
||||
gtk_style_context_get_color(style_ctx, GTK_STATE_FLAG_NORMAL, &fg);
|
||||
gtk_style_context_get_color(style_ctx, &fg);
|
||||
|
||||
int width = gtk_widget_get_allocated_width(widget);
|
||||
int height = gtk_widget_get_allocated_height(widget);
|
||||
gtk_render_background(style_ctx, cr, 0, 0, width, height);
|
||||
|
||||
GtkBorder padding;
|
||||
gtk_style_context_get_padding(style_ctx, GTK_STATE_FLAG_NORMAL, &padding);
|
||||
gtk_style_context_get_padding(style_ctx, &padding);
|
||||
PangoContext *pango = gtk_widget_get_pango_context(widget);
|
||||
PangoLayout *layout = create_text_layout(head, pango, style_ctx);
|
||||
|
||||
@ -191,7 +171,6 @@ void wd_create_overlay(struct wd_output *output) {
|
||||
output->overlay_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_decorated(GTK_WINDOW(output->overlay_window), FALSE);
|
||||
gtk_window_set_resizable(GTK_WINDOW(output->overlay_window), FALSE);
|
||||
gtk_widget_add_events(output->overlay_window, GDK_STRUCTURE_MASK);
|
||||
|
||||
g_signal_connect(output->overlay_window, "realize",
|
||||
G_CALLBACK(window_realize), output);
|
||||
|
668
src/parser.c
668
src/parser.c
@ -1,668 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2019 emersion
|
||||
* Copyright (C) 2019 cyclopsian
|
||||
|
||||
* 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 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 X CONSORTIUM 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Parts of this file are taken from emersion/kanshi:
|
||||
* https://github.com/emersion/kanshi/blob/38d27474b686fcc8324cc5e454741a49577c0988/parser.c
|
||||
* https://github.com/emersion/kanshi/blob/38d27474b686fcc8324cc5e454741a49577c0988/include/config.h
|
||||
*/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "kanshi.h"
|
||||
|
||||
enum kanshi_token_type {
|
||||
KANSHI_TOKEN_LBRACKET,
|
||||
KANSHI_TOKEN_RBRACKET,
|
||||
KANSHI_TOKEN_STR,
|
||||
KANSHI_TOKEN_NEWLINE,
|
||||
KANSHI_TOKEN_COMMENT,
|
||||
};
|
||||
|
||||
struct kanshi_parser {
|
||||
FILE *f;
|
||||
int next;
|
||||
int line, col;
|
||||
|
||||
enum kanshi_token_type tok_type;
|
||||
char tok_str[1024];
|
||||
size_t tok_str_len;
|
||||
};
|
||||
static char commentSign = '#';
|
||||
|
||||
static const char *token_type_str(enum kanshi_token_type t) {
|
||||
switch (t) {
|
||||
case KANSHI_TOKEN_LBRACKET:
|
||||
return "'{'";
|
||||
case KANSHI_TOKEN_RBRACKET:
|
||||
return "'}'";
|
||||
case KANSHI_TOKEN_STR:
|
||||
return "string";
|
||||
case KANSHI_TOKEN_NEWLINE:
|
||||
return "newline";
|
||||
case KANSHI_TOKEN_COMMENT:
|
||||
return "comment";
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static int parser_read_char(struct kanshi_parser *parser) {
|
||||
if (parser->next >= 0) {
|
||||
int ch = parser->next;
|
||||
parser->next = -1;
|
||||
return ch;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
int ch = fgetc(parser->f);
|
||||
if (ch == EOF) {
|
||||
if (errno != 0) {
|
||||
fprintf(stderr, "fgetc failed: %s\n", strerror(errno));
|
||||
} else {
|
||||
return '\0';
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ch == '\n') {
|
||||
parser->line++;
|
||||
parser->col = 0;
|
||||
} else {
|
||||
parser->col++;
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
static int parser_peek_char(struct kanshi_parser *parser) {
|
||||
int ch = parser_read_char(parser);
|
||||
parser->next = ch;
|
||||
return ch;
|
||||
}
|
||||
|
||||
static bool parser_append_tok_ch(struct kanshi_parser *parser, char ch) {
|
||||
// Always keep enough room for a terminating NULL char
|
||||
if (parser->tok_str_len + 1 >= sizeof(parser->tok_str)) {
|
||||
fprintf(stderr, "string too long\n");
|
||||
return false;
|
||||
}
|
||||
parser->tok_str[parser->tok_str_len] = ch;
|
||||
parser->tok_str_len++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parser_read_quoted(struct kanshi_parser *parser) {
|
||||
while (1) {
|
||||
int ch = parser_read_char(parser);
|
||||
if (ch < 0) {
|
||||
return false;
|
||||
} else if (ch == '\0') {
|
||||
fprintf(stderr, "unterminated quoted string\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ch == '"') {
|
||||
parser->tok_str[parser->tok_str_len] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!parser_append_tok_ch(parser, ch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parser_ignore_line(struct kanshi_parser *parser) {
|
||||
while (1) {
|
||||
int ch = parser_read_char(parser);
|
||||
if (ch < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch == '\n' || ch == '\0') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool parser_read_line(struct kanshi_parser *parser) {
|
||||
while (1) {
|
||||
int ch = parser_peek_char(parser);
|
||||
if (ch < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ch == '\n' || ch == '\0') {
|
||||
parser->tok_str[parser->tok_str_len] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!parser_append_tok_ch(parser, parser_read_char(parser))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool parser_read_str(struct kanshi_parser *parser) {
|
||||
while (1) {
|
||||
int ch = parser_peek_char(parser);
|
||||
if (ch < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isspace(ch) || ch == '{' || ch == '}' || ch == '\0') {
|
||||
parser->tok_str[parser->tok_str_len] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!parser_append_tok_ch(parser, parser_read_char(parser))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool parser_next_token(struct kanshi_parser *parser) {
|
||||
while (1) {
|
||||
int ch = parser_read_char(parser);
|
||||
if (ch < 0) {
|
||||
return ch;
|
||||
}
|
||||
|
||||
if (ch == '{') {
|
||||
parser->tok_type = KANSHI_TOKEN_LBRACKET;
|
||||
return true;
|
||||
} else if (ch == '}') {
|
||||
parser->tok_type = KANSHI_TOKEN_RBRACKET;
|
||||
return true;
|
||||
} else if (ch == '\n') {
|
||||
parser->tok_type = KANSHI_TOKEN_NEWLINE;
|
||||
return true;
|
||||
} else if (isspace(ch)) {
|
||||
continue;
|
||||
} else if (ch == '"') {
|
||||
parser->tok_type = KANSHI_TOKEN_STR;
|
||||
parser->tok_str_len = 0;
|
||||
return parser_read_quoted(parser);
|
||||
} else if (ch == commentSign) {
|
||||
parser->tok_type = KANSHI_TOKEN_COMMENT;
|
||||
parser->tok_str_len = 0;
|
||||
return true;
|
||||
} else {
|
||||
parser->tok_type = KANSHI_TOKEN_STR;
|
||||
parser->tok_str[0] = ch;
|
||||
parser->tok_str_len = 1;
|
||||
return parser_read_str(parser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool parser_expect_token(struct kanshi_parser *parser,
|
||||
enum kanshi_token_type want) {
|
||||
if (!parser_next_token(parser)) {
|
||||
return false;
|
||||
}
|
||||
if (parser->tok_type != want) {
|
||||
fprintf(stderr, "expected %s, got %s\n",
|
||||
token_type_str(want), token_type_str(parser->tok_type));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_int(int *dst, const char *str) {
|
||||
char *end;
|
||||
errno = 0;
|
||||
int v = strtol(str, &end, 10);
|
||||
if (errno != 0 || end[0] != '\0' || str[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
*dst = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_mode(struct kanshi_profile_output *output, char *str) {
|
||||
const char *width = strtok(str, "x");
|
||||
const char *height = strtok(NULL, "@");
|
||||
const char *refresh = strtok(NULL, "");
|
||||
|
||||
if (width == NULL || height == NULL) {
|
||||
fprintf(stderr, "invalid output mode: missing width/height\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parse_int(&output->mode.width, width)) {
|
||||
fprintf(stderr, "invalid output mode: invalid width\n");
|
||||
return false;
|
||||
}
|
||||
if (!parse_int(&output->mode.height, height)) {
|
||||
fprintf(stderr, "invalid output mode: invalid height\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (refresh != NULL) {
|
||||
char *end;
|
||||
errno = 0;
|
||||
float v = strtof(refresh, &end);
|
||||
if (errno != 0 || (end[0] != '\0' && strcmp(end, "Hz") != 0) ||
|
||||
str[0] == '\0') {
|
||||
fprintf(stderr, "invalid output mode: invalid refresh rate\n");
|
||||
return false;
|
||||
}
|
||||
output->mode.refresh = v * 1000;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_position(struct kanshi_profile_output *output, char *str) {
|
||||
const char *x = strtok(str, ",");
|
||||
const char *y = strtok(NULL, "");
|
||||
|
||||
if (x == NULL || y == NULL) {
|
||||
fprintf(stderr, "invalid output position: missing x/y\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parse_int(&output->position.x, x)) {
|
||||
fprintf(stderr, "invalid output position: invalid x\n");
|
||||
return false;
|
||||
}
|
||||
if (!parse_int(&output->position.y, y)) {
|
||||
fprintf(stderr, "invalid output position: invalid y\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_float(float *dst, const char *str) {
|
||||
char *end;
|
||||
errno = 0;
|
||||
float v = strtof(str, &end);
|
||||
if (errno != 0 || end[0] != '\0' || str[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
*dst = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_transform(enum wl_output_transform *dst, const char *str) {
|
||||
if (strcmp(str, "normal") == 0) {
|
||||
*dst = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
} else if (strcmp(str, "90") == 0) {
|
||||
*dst = WL_OUTPUT_TRANSFORM_90;
|
||||
} else if (strcmp(str, "180") == 0) {
|
||||
*dst = WL_OUTPUT_TRANSFORM_180;
|
||||
} else if (strcmp(str, "270") == 0) {
|
||||
*dst = WL_OUTPUT_TRANSFORM_270;
|
||||
} else if (strcmp(str, "flipped") == 0) {
|
||||
*dst = WL_OUTPUT_TRANSFORM_FLIPPED;
|
||||
} else if (strcmp(str, "flipped-90") == 0) {
|
||||
*dst = WL_OUTPUT_TRANSFORM_FLIPPED_90;
|
||||
} else if (strcmp(str, "flipped-180") == 0) {
|
||||
*dst = WL_OUTPUT_TRANSFORM_FLIPPED_180;
|
||||
} else if (strcmp(str, "flipped-270") == 0) {
|
||||
*dst = WL_OUTPUT_TRANSFORM_FLIPPED_270;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct kanshi_profile_output *parse_profile_output(
|
||||
struct kanshi_parser *parser) {
|
||||
struct kanshi_profile_output *output = calloc(1, sizeof(*output));
|
||||
|
||||
if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
|
||||
return NULL;
|
||||
}
|
||||
output->name = strdup(parser->tok_str);
|
||||
|
||||
bool has_key = false;
|
||||
enum kanshi_output_field key;
|
||||
while (1) {
|
||||
if (!parser_next_token(parser)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (parser->tok_type) {
|
||||
case KANSHI_TOKEN_STR:
|
||||
if (has_key) {
|
||||
char *value = parser->tok_str;
|
||||
switch (key) {
|
||||
case KANSHI_OUTPUT_MODE:
|
||||
if (!parse_mode(output, value)) {
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case KANSHI_OUTPUT_POSITION:
|
||||
if (!parse_position(output, value)) {
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case KANSHI_OUTPUT_SCALE:
|
||||
if (!parse_float(&output->scale, value)) {
|
||||
fprintf(stderr, "invalid output scale\n");
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case KANSHI_OUTPUT_TRANSFORM:
|
||||
if (!parse_transform(&output->transform, value)) {
|
||||
fprintf(stderr, "invalid output transform\n");
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
has_key = false;
|
||||
output->fields |= key;
|
||||
} else {
|
||||
has_key = true;
|
||||
const char *key_str = parser->tok_str;
|
||||
if (strcmp(key_str, "enable") == 0) {
|
||||
output->enabled = true;
|
||||
output->fields |= KANSHI_OUTPUT_ENABLED;
|
||||
has_key = false;
|
||||
} else if (strcmp(key_str, "disable") == 0) {
|
||||
output->enabled = false;
|
||||
output->fields |= KANSHI_OUTPUT_ENABLED;
|
||||
has_key = false;
|
||||
} else if (strcmp(key_str, "mode") == 0) {
|
||||
key = KANSHI_OUTPUT_MODE;
|
||||
} else if (strcmp(key_str, "position") == 0) {
|
||||
key = KANSHI_OUTPUT_POSITION;
|
||||
} else if (strcmp(key_str, "scale") == 0) {
|
||||
key = KANSHI_OUTPUT_SCALE;
|
||||
} else if (strcmp(key_str, "transform") == 0) {
|
||||
key = KANSHI_OUTPUT_TRANSFORM;
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"unknown directive '%s' in profile output '%s'\n",
|
||||
key_str, output->name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case KANSHI_TOKEN_NEWLINE:
|
||||
return output;
|
||||
case KANSHI_TOKEN_COMMENT:
|
||||
parser_ignore_line(parser);
|
||||
return output;
|
||||
default:
|
||||
fprintf(stderr, "unexpected %s in output\n",
|
||||
token_type_str(parser->tok_type));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct kanshi_profile_command *parse_profile_command(
|
||||
struct kanshi_parser *parser) {
|
||||
// Skip the 'exec' directive.
|
||||
if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!parser_read_line(parser)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (parser->tok_str_len <= 0) {
|
||||
fprintf(stderr, "Ignoring empty command in config file on line %d\n",
|
||||
parser->line);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct kanshi_profile_command *command = calloc(1, sizeof(*command));
|
||||
command->command = strdup(parser->tok_str);
|
||||
return command;
|
||||
}
|
||||
|
||||
static struct kanshi_profile *parse_profile(struct kanshi_parser *parser) {
|
||||
struct kanshi_profile *profile = calloc(1, sizeof(*profile));
|
||||
wl_list_init(&profile->outputs);
|
||||
wl_list_init(&profile->commands);
|
||||
|
||||
// First parse an optional profile name
|
||||
parser->tok_str_len = 0;
|
||||
if (!parser_read_str(parser)) {
|
||||
fprintf(stderr, "expected new profile, got %s\n",
|
||||
token_type_str(parser->tok_type));
|
||||
return NULL;
|
||||
}
|
||||
profile->name = (parser->tok_str_len == 0) ? NULL : strdup(parser->tok_str);
|
||||
|
||||
// Then parse the opening bracket
|
||||
if (!parser_expect_token(parser, KANSHI_TOKEN_LBRACKET)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Use the bracket position to generate a default profile name
|
||||
if (profile->name == NULL) {
|
||||
char generated_name[100];
|
||||
int ret = snprintf(generated_name, sizeof(generated_name),
|
||||
"<anonymous at line %d, col %d>", parser->line, parser->col);
|
||||
if (ret >= 0) {
|
||||
profile->name = strdup(generated_name);
|
||||
} else {
|
||||
profile->name = strdup("<anonymous>");
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the profile commands until the closing bracket
|
||||
while (1) {
|
||||
if (!parser_next_token(parser)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (parser->tok_type) {
|
||||
case KANSHI_TOKEN_RBRACKET:
|
||||
return profile;
|
||||
case KANSHI_TOKEN_STR:;
|
||||
const char *directive = parser->tok_str;
|
||||
if (strcmp(directive, "output") == 0) {
|
||||
struct kanshi_profile_output *output =
|
||||
parse_profile_output(parser);
|
||||
if (output == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Store wildcard outputs at the end of the list
|
||||
if (strcmp(output->name, "*") == 0) {
|
||||
wl_list_insert(profile->outputs.prev, &output->link);
|
||||
} else {
|
||||
wl_list_insert(&profile->outputs, &output->link);
|
||||
}
|
||||
} else if (strcmp(directive, "exec") == 0) {
|
||||
struct kanshi_profile_command *command =
|
||||
parse_profile_command(parser);
|
||||
if (command == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Insert commands at the end to preserve order
|
||||
wl_list_insert(profile->commands.prev, &command->link);
|
||||
} else {
|
||||
fprintf(stderr, "unknown directive '%s' in profile '%s'\n",
|
||||
directive, profile->name);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case KANSHI_TOKEN_NEWLINE:
|
||||
break; // No-op
|
||||
case KANSHI_TOKEN_COMMENT:
|
||||
parser_ignore_line(parser);
|
||||
break; // No-op
|
||||
default:
|
||||
fprintf(stderr, "unexpected %s in profile '%s'\n",
|
||||
token_type_str(parser->tok_type), profile->name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct kanshi_config *_parse_config(struct kanshi_parser *parser) {
|
||||
struct kanshi_config *config = calloc(1, sizeof(*config));
|
||||
wl_list_init(&config->profiles);
|
||||
|
||||
while (1) {
|
||||
int ch = parser_peek_char(parser);
|
||||
if (ch < 0) {
|
||||
return NULL;
|
||||
} else if (ch == 0) {
|
||||
return config;
|
||||
} else if (ch == commentSign) {
|
||||
parser_ignore_line(parser);
|
||||
continue;
|
||||
} else if (isspace(ch)) {
|
||||
parser_read_char(parser);
|
||||
continue;
|
||||
}
|
||||
|
||||
struct kanshi_profile *profile = parse_profile(parser);
|
||||
if (!profile) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Inset at the end to preserve ordering
|
||||
wl_list_insert(config->profiles.prev, &profile->link);
|
||||
}
|
||||
}
|
||||
|
||||
struct kanshi_config *kanshi_parse_config(const char *path) {
|
||||
FILE *f = fopen(path, "r");
|
||||
if (f == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct kanshi_parser parser = {
|
||||
.f = f,
|
||||
.next = -1,
|
||||
.line = 1,
|
||||
};
|
||||
|
||||
struct kanshi_config *config = _parse_config(&parser);
|
||||
fclose(f);
|
||||
if (config == NULL) {
|
||||
fprintf(stderr, "failed to parse config file: "
|
||||
"error on line %d, column %d\n", parser.line, parser.col);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
static const char *transform_to_string(enum wl_output_transform transform) {
|
||||
switch (transform) {
|
||||
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||
return "normal";
|
||||
case WL_OUTPUT_TRANSFORM_90:
|
||||
return "90";
|
||||
case WL_OUTPUT_TRANSFORM_180:
|
||||
return "180";
|
||||
case WL_OUTPUT_TRANSFORM_270:
|
||||
return "270";
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED:
|
||||
return "flipped";
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||
return "flipped-90";
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||
return "flipped-180";
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||
return "flipped-270";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void kanshi_save_config(const char *path, struct kanshi_config *config) {
|
||||
FILE *f = fopen(path, "w");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "fopen: %s: %s\n", path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "# DO NOT EDIT - file autogenerated by wdisplays\n\n");
|
||||
|
||||
struct kanshi_profile *profile;
|
||||
wl_list_for_each(profile, &config->profiles, link) {
|
||||
fprintf(f, "{\n");
|
||||
struct kanshi_profile_output *profile_output;
|
||||
wl_list_for_each(profile_output, &profile->outputs, link) {
|
||||
fprintf(f, "\toutput \"%s\"", profile_output->name);
|
||||
if (profile_output->fields & KANSHI_OUTPUT_ENABLED) {
|
||||
fprintf(f, " enable");
|
||||
if (profile_output->fields & KANSHI_OUTPUT_MODE) {
|
||||
fprintf(f, " mode %dx%d@%0.3fHz",
|
||||
profile_output->mode.width, profile_output->mode.height,
|
||||
profile_output->mode.refresh / 1000.f);
|
||||
}
|
||||
if (profile_output->fields & KANSHI_OUTPUT_POSITION) {
|
||||
fprintf(f, " position %d,%d",
|
||||
profile_output->position.x, profile_output->position.y);
|
||||
}
|
||||
if (profile_output->fields & KANSHI_OUTPUT_SCALE) {
|
||||
fprintf(f, " scale %f", profile_output->scale);
|
||||
}
|
||||
if (profile_output->fields & KANSHI_OUTPUT_TRANSFORM) {
|
||||
fprintf(f, " transform %s",
|
||||
transform_to_string(profile_output->transform));
|
||||
}
|
||||
} else {
|
||||
fprintf(f, " disable");
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
fprintf(f, "}\n\n");
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void kanshi_destroy_config(struct kanshi_config *config) {
|
||||
struct kanshi_profile *profile, *tmp_profile;
|
||||
wl_list_for_each_safe(profile, tmp_profile, &config->profiles, link) {
|
||||
struct kanshi_profile_output *output, *tmp_output;
|
||||
wl_list_for_each_safe(output, tmp_output, &profile->outputs, link) {
|
||||
free(output->name);
|
||||
wl_list_remove(&output->link);
|
||||
free(output);
|
||||
}
|
||||
struct kanshi_profile_command *command, *tmp_command;
|
||||
wl_list_for_each_safe(command, tmp_command, &profile->commands, link) {
|
||||
free(command->command);
|
||||
wl_list_remove(&command->link);
|
||||
free(command);
|
||||
}
|
||||
wl_list_remove(&profile->link);
|
||||
if (profile->name != NULL) {
|
||||
free(profile->name);
|
||||
}
|
||||
free(profile);
|
||||
}
|
||||
free(config);
|
||||
}
|
28
src/render.c
28
src/render.c
@ -1,25 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 cyclopsian
|
||||
|
||||
* 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 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 X CONSORTIUM 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.
|
||||
*/
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright (C) 2019 cyclopsian */
|
||||
|
||||
#include "wdisplays.h"
|
||||
|
||||
@ -75,6 +55,7 @@ struct wd_gl_data {
|
||||
};
|
||||
|
||||
static const char *color_vertex_shader_src = "\
|
||||
precision mediump float;\n\
|
||||
attribute vec2 position;\n\
|
||||
attribute vec4 color;\n\
|
||||
varying vec4 color_out;\n\
|
||||
@ -86,12 +67,14 @@ void main(void) {\n\
|
||||
}";
|
||||
|
||||
static const char *color_fragment_shader_src = "\
|
||||
precision mediump float;\n\
|
||||
varying vec4 color_out;\n\
|
||||
void main(void) {\n\
|
||||
gl_FragColor = color_out;\n\
|
||||
}";
|
||||
|
||||
static const char *texture_vertex_shader_src = "\
|
||||
precision mediump float;\n\
|
||||
attribute vec2 position;\n\
|
||||
attribute vec2 uv;\n\
|
||||
varying vec2 uv_out;\n\
|
||||
@ -103,6 +86,7 @@ void main(void) {\n\
|
||||
}";
|
||||
|
||||
static const char *texture_fragment_shader_src = "\
|
||||
precision mediump float;\n\
|
||||
varying vec2 uv_out;\n\
|
||||
uniform sampler2D texture;\n\
|
||||
uniform mat4 color_transform;\n\
|
||||
|
@ -1,26 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2019 cyclopsian
|
||||
* Copyright (C) 2017-2019 emersion
|
||||
|
||||
* 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 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 X CONSORTIUM 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.
|
||||
*/
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/* Copyright (C) 2019 cyclopsian
|
||||
* Copyright (C) 2017-2019 emersion */
|
||||
|
||||
/*
|
||||
* Parts of this file are taken from emersion/kanshi:
|
||||
@ -28,8 +8,10 @@
|
||||
* https://github.com/emersion/kanshi/blob/38d27474b686fcc8324cc5e454741a49577c0988/include/config.h
|
||||
*/
|
||||
|
||||
#ifndef WDISPLAYS_WDISPLAYS_H
|
||||
#define WDISPLAYS_WDISPLAYS_H
|
||||
#ifndef WDISPLAY_WDISPLAY_H
|
||||
#define WDISPLAY_WDISPLAY_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define HEADS_MAX 64
|
||||
#define HOVER_USECS (100 * 1000)
|
||||
@ -250,6 +232,7 @@ struct wd_state {
|
||||
struct wd_render_data render;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Creates the application state structure.
|
||||
*/
|
||||
@ -352,16 +335,16 @@ void wd_gl_cleanup(struct wd_gl_data *res);
|
||||
* Create an overlay on the screen that contains a textual description of the
|
||||
* output. This is to help the user identify the outputs visually.
|
||||
*/
|
||||
void wd_create_overlay(struct wd_output *output);
|
||||
static inline void wd_create_overlay(struct wd_output *output) {}
|
||||
|
||||
/*
|
||||
* Forces redrawing of the screen overlay on the given output.
|
||||
*/
|
||||
void wd_redraw_overlay(struct wd_output *output);
|
||||
static inline void wd_redraw_overlay(struct wd_output *output) {}
|
||||
|
||||
/*
|
||||
* Destroys the screen overlay on the given output.
|
||||
*/
|
||||
void wd_destroy_overlay(struct wd_output *output);
|
||||
static inline void wd_destroy_overlay(struct wd_output *output) {}
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user