Merge pull request #2717 from dotCooCoo/fix/linux-tablet-xwayland

Linux Display Server: XWayland when a pen is attached, where a drawing tablet's pen draws
This commit is contained in:
BFlat
2026-10-18 01:55:46 +07:00
committed by GitHub
32 changed files with 250 additions and 27 deletions
+4 -2
View File
@@ -194,8 +194,10 @@ drag-and-drop, set **Preferences › Performance › Linux Display Server** to *
the app with `WAYLAND_DISPLAY= vectorcraft`: the window then opens through XWayland, where drops work
(see [`docs/development.md`](docs/development.md#linux-wayland-and-x11)).
On Linux under KDE Plasma 6.3 or later with Wayland, a drawing tablet's pen moves the cursor but VectorCraft doesn't
respond to it yet (#491). Open the window through XWayland instead, with the same preference or `WAYLAND_DISPLAY= vectorcraft` (see
On Linux with Wayland, under KDE Plasma 6.3 or later and under GNOME, VectorCraft doesn't respond to a drawing tablet's
pen yet (#491, #2028). Open the window through XWayland instead, where the pen works as a mouse: set the same
preference to **X11 (XWayland)**, or to **X11 (XWayland) When a Pen Is Attached** to use XWayland only when a pen is
attached as VectorCraft starts, and restart; or start the app with `WAYLAND_DISPLAY= vectorcraft` (see
[`docs/development.md`](docs/development.md#linux-wayland-and-x11)).
### Use it from Claude Code and other agents
+8 -3
View File
@@ -788,14 +788,19 @@ fn main() -> std::process::ExitCode {
let custom_titlebar = custom_titlebar(saved.as_ref());
let options = native_options(custom_titlebar);
// winit picks Wayland whenever `WAYLAND_DISPLAY` is set; Preferences › Performance › Linux
// Display Server › X11 opens the window through XWayland instead, where file drops work (#1151).
// Display Server › X11 opens the window through XWayland instead, where file drops work (#1151),
// and X11 (XWayland) When a Pen Is Attached does so when a pen is attached, where the pen draws (#2028).
#[cfg(target_os = "linux")]
let options = {
let mut options = options;
let pref = saved.as_ref().and_then(|ui| ui.engine_prefs.get("linuxDisplayServer")).and_then(serde_json::Value::as_str);
if xwayland::wanted(pref, |name| std::env::var_os(name)) {
if xwayland::wanted(pref, |name| std::env::var_os(name), xwayland::pen_attached) {
if xwayland::x11_libraries_found() {
log::info!("opening the window through XWayland (Preferences › Performance › Linux Display Server)");
if pref == Some("x11WithPen") {
log::info!("pen found: opening the window through XWayland (Preferences › Performance › Linux Display Server)");
} else {
log::info!("opening the window through XWayland (Preferences › Performance › Linux Display Server)");
}
use winit::platform::x11::EventLoopBuilderExtX11 as _;
options.event_loop_builder = Some(Box::new(|builder| {
builder.with_x11();
+129 -17
View File
@@ -1,11 +1,15 @@
//! Linux: Preferences › Performance › Linux Display Server › X11 opens the window through XWayland
//! on a Wayland session, where dropped files arrive (winit 0.30 has no Wayland drag and drop,
//! #1151). Only when XWayland can run the window: an X display is set and the X11 libraries winit
//! loads at runtime are installed. Otherwise the window opens on Wayland, so a missing library
//! never keeps the app from starting.
//! #1151). X11 (XWayland) When a Pen Is Attached does so only when a pen is attached as the app
//! starts: winit 0.30 doesn't bind the Wayland tablet protocol, and XWayland hands the pen to X11
//! windows as a pointer (#2028). Only when XWayland can run the window: an X display is set and
//! the X11 libraries winit loads at runtime are installed. Otherwise the window opens on Wayland,
//! so a missing library never keeps the app from starting.
//!
//! The library check comes from PhotoCraft's `linux_libs.rs`: no `dlopen` (that needs `unsafe`),
//! just the dynamic linker's cache (`ldconfig -p`) and the usual library directories.
//! just the dynamic linker's cache (`ldconfig -p`) and the usual library directories. The pen
//! check comes from PhotoCraft's `tablet.rs`: the input devices the kernel lists in
//! `/proc/bus/input/devices`.
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
use std::ffi::OsString;
@@ -16,12 +20,50 @@ use std::path::{Path, PathBuf};
const X11_LIBS: &[&str] =
&["libX11.so.6", "libX11-xcb.so.1", "libxcb.so.1", "libXcursor.so.1", "libXi.so.6", "libxkbcommon.so.0", "libxkbcommon-x11.so.0"];
/// Whether the saved `linuxDisplayServer` and the environment ask for XWayland: `x11` chosen, a
/// Wayland session (winit picks Wayland whenever `WAYLAND_DISPLAY` or `WAYLAND_SOCKET` is set)
/// and an X display to open.
pub fn wanted(pref: Option<&str>, var: impl Fn(&str) -> Option<OsString>) -> bool {
/// Whether the saved `linuxDisplayServer` and the environment ask for XWayland: `x11` chosen, or
/// `x11WithPen` chosen and `pen` finding a pen; a Wayland session (winit picks Wayland whenever
/// `WAYLAND_DISPLAY` or `WAYLAND_SOCKET` is set); and an X display to open. `pen` runs only for
/// `x11WithPen`, after the session checks.
pub fn wanted(pref: Option<&str>, var: impl Fn(&str) -> Option<OsString>, pen: impl FnOnce() -> bool) -> bool {
let set = |name: &str| var(name).is_some_and(|v| !v.is_empty());
pref == Some("x11") && (set("WAYLAND_DISPLAY") || set("WAYLAND_SOCKET")) && set("DISPLAY")
let session = (set("WAYLAND_DISPLAY") || set("WAYLAND_SOCKET")) && set("DISPLAY");
match pref {
Some("x11") => session,
Some("x11WithPen") => session && pen(),
_ => false,
}
}
/// `BTN_TOOL_PEN` (linux/input-event-codes.h): the key that a pen tablet's stylus, a pen display's
/// pen or a pen-enabled touchscreen's pen reports.
const BTN_TOOL_PEN: u32 = 0x140;
/// `ABS_X` and `ABS_Y` (linux/input-event-codes.h): the absolute axes every pen reports.
const ABS_X: u32 = 0x00;
const ABS_Y: u32 = 0x01;
/// Is a pen attached? Reads `/proc/bus/input/devices`; a file that can't be read has no pen.
pub fn pen_attached() -> bool {
std::fs::read_to_string("/proc/bus/input/devices").is_ok_and(|devices| has_pen(&devices))
}
/// Is a pen among the input devices `/proc/bus/input/devices` lists? A pen is a device whose
/// `B: KEY=` bitmap has `BTN_TOOL_PEN` and whose `B: ABS=` bitmap has `ABS_X` and `ABS_Y`, as
/// libinput asks of a tablet tool. A virtual keyboard made with uinput can claim every key code,
/// `BTN_TOOL_PEN` included, but has no absolute axes.
fn has_pen(devices: &str) -> bool {
devices.split("\n\n").any(|device| {
let bitmap = |name: &str| device.lines().find_map(|l| l.strip_prefix(name));
bitmap("B: KEY=").is_some_and(|keys| bit_set(keys, BTN_TOOL_PEN))
&& bitmap("B: ABS=").is_some_and(|axes| bit_set(axes, ABS_X) && bit_set(axes, ABS_Y))
})
}
/// Bit `bit` of a `/proc/bus/input/devices` bitmap: hex words of the kernel's `long`, most
/// significant first. Short or malformed bitmaps have no bits set.
fn bit_set(bitmap: &str, bit: u32) -> bool {
let bits = usize::BITS;
let word = usize::try_from(bit / bits).unwrap_or(usize::MAX);
bitmap.split_whitespace().rev().nth(word).and_then(|w| u64::from_str_radix(w, 16).ok()).is_some_and(|w| (w >> (bit % bits)) & 1 == 1)
}
/// Are all the libraries an X11 window needs found on this system? Libraries the check can't find
@@ -98,18 +140,88 @@ mod tests {
move |k| pairs.iter().find(|(name, _)| *name == k).map(|(_, v)| OsString::from(v))
}
fn no_probe() -> bool {
panic!("the pen check ran")
}
#[test]
fn xwayland_only_when_chosen_on_wayland_with_an_x_display() {
let wayland = env(&[("WAYLAND_DISPLAY", "wayland-0"), ("DISPLAY", ":0")]);
assert!(wanted(Some("x11"), &wayland));
assert!(wanted(Some("x11"), env(&[("WAYLAND_SOCKET", "3"), ("DISPLAY", ":0")])));
assert!(!wanted(Some("automatic"), &wayland));
assert!(!wanted(None, &wayland));
assert!(!wanted(Some("wayland"), &wayland));
assert!(wanted(Some("x11"), &wayland, no_probe));
assert!(wanted(Some("x11"), env(&[("WAYLAND_SOCKET", "3"), ("DISPLAY", ":0")]), no_probe));
assert!(!wanted(Some("automatic"), &wayland, no_probe));
assert!(!wanted(None, &wayland, no_probe));
assert!(!wanted(Some("wayland"), &wayland, no_probe));
// An X11 session already opens on X11; without an X display there is no XWayland to use.
assert!(!wanted(Some("x11"), env(&[("DISPLAY", ":0")])));
assert!(!wanted(Some("x11"), env(&[("WAYLAND_DISPLAY", "wayland-0"), ("DISPLAY", "")])));
assert!(!wanted(Some("x11"), env(&[("WAYLAND_DISPLAY", "wayland-0")])));
assert!(!wanted(Some("x11"), env(&[("DISPLAY", ":0")]), no_probe));
assert!(!wanted(Some("x11"), env(&[("WAYLAND_DISPLAY", "wayland-0"), ("DISPLAY", "")]), no_probe));
assert!(!wanted(Some("x11"), env(&[("WAYLAND_DISPLAY", "wayland-0")]), no_probe));
}
/// X11 (XWayland) When a Pen Is Attached (#2028): XWayland on a Wayland session with an X
/// display when a pen is found, the session's own display server otherwise. The pen check
/// runs only on such a session.
#[test]
fn the_pen_choice_opens_xwayland_only_with_a_pen() {
let wayland = env(&[("WAYLAND_DISPLAY", "wayland-0"), ("DISPLAY", ":0")]);
assert!(wanted(Some("x11WithPen"), &wayland, || true));
assert!(wanted(Some("x11WithPen"), env(&[("WAYLAND_SOCKET", "3"), ("DISPLAY", ":0")]), || true));
assert!(!wanted(Some("x11WithPen"), &wayland, || false));
assert!(!wanted(Some("x11WithPen"), env(&[("DISPLAY", ":0")]), no_probe));
assert!(!wanted(Some("x11WithPen"), env(&[("WAYLAND_DISPLAY", "wayland-0")]), no_probe));
assert!(!wanted(Some("x11WithPen"), env(&[("WAYLAND_DISPLAY", "wayland-0"), ("DISPLAY", "")]), no_probe));
}
/// `/proc/bus/input/devices` of a laptop with a Wacom Intuos Pro attached (64-bit longs: the
/// pen's `KEY=1c03 0 0 0 0 0` has bit 0x140, `BTN_TOOL_PEN`), from PhotoCraft's tests.
const WITH_PEN: &str = "I: Bus=0011 Vendor=0001 Product=0001 Version=ab83\n\
N: Name=\"AT Translated Set 2 keyboard\"\n\
H: Handlers=sysrq kbd event3 leds\n\
B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe\n\
\n\
I: Bus=0018 Vendor=06cb Product=ce26 Version=0100\n\
N: Name=\"SYNA2B33:00 06CB:CE26 Touchpad\"\n\
H: Handlers=mouse1 event7\n\
B: KEY=e520 10000 0 0 0 0\n\
B: ABS=2e0800000000003\n\
\n\
I: Bus=0003 Vendor=056a Product=0357 Version=0110\n\
N: Name=\"Wacom Intuos Pro M Pen\"\n\
H: Handlers=mouse2 event10\n\
B: KEY=1c03 0 0 0 0 0\n\
B: ABS=1000f000003\n";
/// A uinput virtual keyboard that claims every key code, `BTN_TOOL_PEN` included, and has no
/// absolute axes (`EV=20013`, no `B: ABS=` line), from PhotoCraft's tests.
const VIRTUAL_KEYBOARD: &str = "I: Bus=0003 Vendor=1234 Product=5678 Version=0111\n\
N: Name=\"Virtual Keyboard\"\n\
H: Handlers=sysrq rfkill kbd event256 leds \n\
B: EV=20013\n\
B: KEY=ffffffffff 0 7ffffff000301ff 1000f7fffffff 7fe001fffff000f 7ffffffffffffff ffffffff0003fdff 7fff8fff00ff03ff 1ffffffffffff07 ffffffffffffffff ffffffffffefffff fffffffffffffffe\n";
#[test]
fn a_pen_is_a_device_with_btn_tool_pen_and_absolute_axes() {
assert!(has_pen(WITH_PEN));
let without = WITH_PEN.split("\n\n").take(2).collect::<Vec<_>>().join("\n\n");
assert!(!has_pen(&without), "a touchpad's BTN_TOOL_FINGER (0x145) is not a pen");
// A pen display reports the pen with its own axes.
assert!(has_pen("N: Name=\"Wacom One Pen\"\nB: KEY=1c03 0 0 0 0 0\nB: ABS=3\n"));
// Axes without ABS_X or ABS_Y, or no axes at all, are no pen.
assert!(!has_pen("B: KEY=1c03 0 0 0 0 0\nB: ABS=1000f000002\n"));
assert!(!has_pen("B: KEY=1c03 0 0 0 0 0\nB: ABS=1000f000001\n"));
let pen_without_axes = WITH_PEN.lines().filter(|l| !l.starts_with("B: ABS=1000f")).collect::<Vec<_>>().join("\n");
assert!(!has_pen(&pen_without_axes));
// The virtual keyboard claims the key but is no pen, and a real pen beside it is still found.
assert!(VIRTUAL_KEYBOARD.lines().find_map(|l| l.strip_prefix("B: KEY=")).is_some_and(|k| bit_set(k, BTN_TOOL_PEN)));
assert!(!has_pen(VIRTUAL_KEYBOARD));
assert!(has_pen(&format!("{VIRTUAL_KEYBOARD}\n{WITH_PEN}")));
// Short or malformed bitmaps are no pen, never a panic.
for keys in ["", "B: KEY=", "B: KEY=1", "B: KEY=zz 0 0 0 0 0", "B: KEY=ffffffffffffffffff 0 0 0 0 0", "B: KEY=0 0 0 0 0 0 0 0"] {
assert!(!has_pen(&format!("{keys}\nB: ABS=3")), "{keys:?}");
}
for abs in ["B: ABS=", "B: ABS=zz", "B: ABS=ffffffffffffffffff", "B: ABS=0 0"] {
assert!(!has_pen(&format!("B: KEY=1c03 0 0 0 0 0\n{abs}")), "{abs:?}");
}
}
#[test]
+4 -2
View File
@@ -89,8 +89,10 @@ pub const GPU_PREFERENCES: &[(&str, &str)] =
&[("automatic", "Automatic"), ("lowPower", "Power Saving (integrated)"), ("highPerformance", "High Performance (discrete)")];
/// Performance › Linux Display Server (`linuxDisplayServer`), read by the desktop app at startup:
/// `x11` opens the window through XWayland on a Wayland session, where dropped files arrive
/// (winit 0.30 has no Wayland drag and drop, #1151).
const LINUX_DISPLAY_SERVERS: &[(&str, &str)] = &[("automatic", "Automatic"), ("x11", "X11 (XWayland)")];
/// (winit 0.30 has no Wayland drag and drop, #1151); `x11WithPen` does the same only when a pen is
/// attached as the app starts: under XWayland a drawing tablet's pen draws as a mouse (#2028).
const LINUX_DISPLAY_SERVERS: &[(&str, &str)] =
&[("automatic", "Automatic"), ("x11", "X11 (XWayland)"), ("x11WithPen", "X11 (XWayland) When a Pen Is Attached")];
const BLACK: &[(&str, &str)] = &[("accurate", "Display All Blacks Accurately"), ("rich", "Display All Blacks as Rich Black")];
const BLACK_OUT: &[(&str, &str)] = &[("accurate", "Output All Blacks Accurately"), ("rich", "Output All Blacks as Rich Black")];
+2 -1
View File
@@ -918,7 +918,8 @@ pub struct Prefs {
/// Rasterizer worker threads; -1 = automatic.
pub render_threads: i32,
/// Linux: `automatic` opens the window on the session's display server; `x11` opens it through
/// XWayland on a Wayland session, where dropped files arrive (#1151). Read when the app starts.
/// XWayland on a Wayland session, where dropped files arrive (#1151); `x11WithPen` opens it
/// through XWayland only when a pen is attached (#2028). Read when the app starts.
pub linux_display_server: String,
// File Handling
pub background_save: bool,
+14
View File
@@ -240,6 +240,20 @@ fn linux_display_server_defaults_to_automatic_and_validates() {
assert_eq!(s.prefs.linux_display_server, "automatic");
}
/// Linux Display Server's pen choice (#2028): set by value or by label, saved, and off by default.
#[test]
fn linux_display_server_has_a_choice_for_an_attached_pen() {
let mut s = Session::new();
s.execute("prefs.set", &json!({"key": "linuxDisplayServer", "value": "x11WithPen"})).unwrap();
assert_eq!(s.prefs.linux_display_server, "x11WithPen");
assert_eq!(s.prefs.to_json()["linuxDisplayServer"], "x11WithPen");
s.execute("prefs.set", &json!({"key": "linuxDisplayServer", "value": "automatic"})).unwrap();
s.execute("prefs.set", &json!({"key": "linuxDisplayServer", "value": "X11 (XWayland) When a Pen Is Attached"})).unwrap();
assert_eq!(s.prefs.linux_display_server, "x11WithPen");
s.execute("prefs.reset", &json!({"category": "Performance"})).unwrap();
assert_eq!(s.prefs.linux_display_server, "automatic");
}
/// Selection & Anchor Display › Tolerance, Object Selection by Path Only and Command Click to
/// Select Objects Behind, and General › Double Click To Isolate, as the Selection tool sees them
/// (#394).
+2
View File
@@ -4462,6 +4462,8 @@ lighting Light Светлина
@msg missing panel layout липсващо оформление на панели
@msg unsupported native docking operation неподдържана вградена операция за скачване
Linux Display Server Дисплейен сървър за Linux
X11 (XWayland) When a Pen Is Attached X11 (XWayland) при свързана писалка
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Ако писалката на графичния таблет не рисува, изберете опция X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Ruler Origin Начало на линийката
Reset Ruler Origin Нулиране началото на линийката
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3607,6 +3607,8 @@ axis Both Obě
Star Hvězda
Power Saving (integrated) Úsporný režim (integrovaná)
Target Cíl
X11 (XWayland) When a Pen Is Attached X11 (XWayland) při připojeném peru
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Pokud pero grafického tabletu nekreslí, zvolte možnost X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Print as Bitmap Tisknout jako bitmapu
Draft Koncept
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3377,6 +3377,8 @@ axis Both Begge
Star Stjerne
Power Saving (integrated) Strømbesparende (integreret)
Target Mål
X11 (XWayland) When a Pen Is Attached X11 (XWayland), når en pen er tilsluttet
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Hvis pennen til en tegneplade ikke tegner, skal du vælge en X11 (XWayland)-indstilling.
X11 (XWayland) X11 (XWayland)
Print as Bitmap Udskriv som bitmap
Draft Udkast
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4525,6 +4525,8 @@ lighting Light Licht
@msg missing panel layout Bedienfeldanordnung fehlt
@msg unsupported native docking operation Nicht unterstützte native Docking-Operation
Linux Display Server Linux-Anzeigeserver
X11 (XWayland) When a Pen Is Attached X11 (XWayland), wenn ein Stift angeschlossen ist
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Wenn der Stift eines Grafiktabletts nicht zeichnet, wählen Sie eine Option mit X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Ruler Origin Linealursprung
Reset Ruler Origin Linealursprung zurücksetzen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4951,6 +4951,8 @@ effect Points Σημεία
Unicode Unicode
Unicode (UTF-16) Unicode (UTF-16)
Unicode (UTF-8) Unicode (UTF-8)
X11 (XWayland) When a Pen Is Attached X11 (XWayland) όταν είναι συνδεδεμένη πένα
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Αν η πένα μιας ταμπλέτας σχεδίασης δεν σχεδιάζει, επιλέξτε μία από τις ρυθμίσεις X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Mac Mac
Phone 360×800 Phone 360×800
Can't render this file because it contains an unexpected character in line 2000 and column 19.
+2
View File
@@ -4529,6 +4529,8 @@ lighting Light Luz
@msg missing panel layout Falta la distribución de paneles
@msg unsupported native docking operation Operación nativa de anclaje no admitida
Linux Display Server Servidor de pantalla de Linux
X11 (XWayland) When a Pen Is Attached X11 (XWayland) cuando hay un lápiz conectado
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Si el lápiz de una tableta digitalizadora no dibuja, elige una opción X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Ruler Origin Origen de las reglas
Reset Ruler Origin Restablecer el origen de las reglas
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3542,6 +3542,8 @@ axis Both Molemmat
Star Tähti
Power Saving (integrated) Virransäästö (integroitu)
Target Kohde
X11 (XWayland) When a Pen Is Attached X11 (XWayland), kun kynä on liitetty
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Jos piirtopöydän kynä ei piirrä, valitse jokin X11 (XWayland) -vaihtoehto.
X11 (XWayland) X11 (XWayland)
Print as Bitmap Tulosta bittikarttana
Draft Vedos
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4539,6 +4539,8 @@ lighting Light Lumière
@msg missing panel layout Disposition des panneaux manquante
@msg unsupported native docking operation Opération native d’ancrage non prise en charge
Linux Display Server Serveur d’affichage Linux
X11 (XWayland) When a Pen Is Attached X11 (XWayland) quand un stylet est branché
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Si le stylet d’une tablette graphique ne dessine pas, choisissez une option X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Ruler Origin Origine des règles
Reset Ruler Origin Réinitialiser l’origine des règles
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4212,6 +4212,8 @@ mark-style Japanese Japán
Wrinkle Tool Gyűrés
Write Layers Rétegek írása
x Height x-magasság
X11 (XWayland) When a Pen Is Attached X11 (XWayland), ha toll csatlakozik
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Ha a grafikus tábla tolla nem rajzol, válassz egy X11 (XWayland) beállítást.
X11 (XWayland) X11 (XWayland)
X: X:
XML ID XML-azonosító
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3377,6 +3377,8 @@ axis Both Keduanya
Star Bintang
Power Saving (integrated) Hemat daya (terintegrasi)
Target Target
X11 (XWayland) When a Pen Is Attached X11 (XWayland) saat pena terpasang
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Jika pena tablet gambar tidak menggambar, pilih opsi X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Print as Bitmap Cetak sebagai bitmap
Draft Draf
1 # Indonesian (id) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
3377
3378
3379
3380
3381
3382
3383
3384
+2
View File
@@ -4570,6 +4570,8 @@ lighting Light Luce
@msg missing panel layout Disposizione dei pannelli mancante
@msg unsupported native docking operation Operazione nativa di ancoraggio non supportata
Linux Display Server Server di visualizzazione Linux
X11 (XWayland) When a Pen Is Attached X11 (XWayland) quando è collegata una penna
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Se la penna di una tavoletta grafica non disegna, scegli un'opzione X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Ruler Origin Origine dei righelli
Reset Ruler Origin Ripristina origine dei righelli
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4610,6 +4610,8 @@ lighting Light ライト
@msg missing panel layout パネル配置がありません
@msg unsupported native docking operation ネイティブドッキング操作は未対応です
Linux Display Server Linux ディスプレイサーバー
X11 (XWayland) When a Pen Is Attached ペン接続時は X11 (XWayland)
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. ペンタブレットのペンで描画できない場合は、X11 (XWayland) のいずれかを選択してください。
X11 (XWayland) X11 (XWayland)
Ruler Origin 定規の原点
Reset Ruler Origin 定規の原点をリセット
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4536,6 +4536,8 @@ lighting Light 광원
@msg missing panel layout 패널 레이아웃이 없습니다
@msg unsupported native docking operation 지원되지 않는 기본 도킹 작업
Linux Display Server Linux 디스플레이 서버
X11 (XWayland) When a Pen Is Attached 펜 연결 시 X11 (XWayland)
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. 드로잉 태블릿의 펜으로 그려지지 않으면 X11 (XWayland) 옵션을 선택하세요.
X11 (XWayland) X11 (XWayland)
Artistic 예술 효과
Film Grain… 필름 그레인…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3391,6 +3391,8 @@ axis Both Beide
Star Ster
Power Saving (integrated) Energiebesparend (geïntegreerd)
Target Doel
X11 (XWayland) When a Pen Is Attached X11 (XWayland) als er een pen is aangesloten
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Als de pen van een tekentablet niet tekent, kies dan een X11 (XWayland)-optie.
X11 (XWayland) X11 (XWayland)
Print as Bitmap Afdrukken als bitmap
Draft Concept
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3449,6 +3449,8 @@ axis Both Obie
Star Gwiazda
Power Saving (integrated) Oszczędzanie energii (zintegrowany)
Target Cel
X11 (XWayland) When a Pen Is Attached X11 (XWayland), gdy podłączone jest pióro
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Jeśli pióro tabletu graficznego nie rysuje, wybierz opcję X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Print as Bitmap Drukuj jako bitmapę
Draft Robocza
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4560,6 +4560,8 @@ axis Both Ambos
Right Diagonal Diagonal Direita
Left Diagonal Diagonal Esquerda
Linux Display Server Servidor de Exibição do Linux
X11 (XWayland) When a Pen Is Attached X11 (XWayland) Quando Houver uma Caneta Conectada
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Se a caneta de uma mesa digitalizadora não desenhar, escolha uma opção X11 (XWayland).
X11 (XWayland) X11 (XWayland)
@id pathfinder.add Unir
3 x 3 Average Média 3 x 3
Can't render this file because it contains an unexpected character in line 6 and column 47.
+2
View File
@@ -4566,6 +4566,8 @@ lighting Light Свет
@msg missing panel layout Отсутствует расположение панелей
@msg unsupported native docking operation Неподдерживаемая собственная операция прикрепления
Linux Display Server Дисплейный сервер Linux
X11 (XWayland) When a Pen Is Attached X11 (XWayland) при подключенном пере
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Если перо графического планшета не рисует, выберите вариант X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Ruler Origin Начало координат линеек
Reset Ruler Origin Сбросить начало координат линеек
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4461,6 +4461,8 @@ lighting Light Ljus
@msg missing panel layout panellayout saknas
@msg unsupported native docking operation inbyggd dockningsåtgärd som inte stöds
Linux Display Server Linux-skärmserver
X11 (XWayland) When a Pen Is Attached X11 (XWayland) när en penna är ansluten
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Om ritplattans penna inte ritar väljer du ett X11 (XWayland)-alternativ.
X11 (XWayland) X11 (XWayland)
Ruler Origin Linjalens origo
Reset Ruler Origin Återställ linjalens origo
Can't render this file because it contains an unexpected character in line 5 and column 25.
+2
View File
@@ -4550,6 +4550,8 @@ lighting Light Світло
@msg missing panel layout Відсутнє розташування панелей
@msg unsupported native docking operation Непідтримувана власна операція прикріплення
Linux Display Server Дисплейний сервер Linux
X11 (XWayland) When a Pen Is Attached X11 (XWayland), коли підключено перо
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Якщо перо графічного планшета не малює, виберіть варіант X11 (XWayland).
X11 (XWayland) X11 (XWayland)
Ruler Origin Початок координат лінійок
Reset Ruler Origin Скинути початок координат лінійок
Can't render this file because it contains an unexpected character in line 725 and column 40.
+2
View File
@@ -3873,6 +3873,8 @@ mark-style Japanese Kiểu Nhật
Workspace {i} Không gian làm việc {i}
Wrinkle Tool Công cụ nhăn
Write Layers Ghi các lớp
X11 (XWayland) When a Pen Is Attached X11 (XWayland) khi có bút được kết nối
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. Nếu bút của bảng vẽ không vẽ được, hãy chọn một tùy chọn X11 (XWayland).
X11 (XWayland) X11 (XWayland)
XML ID XML ID
Yards Yard
1 # Vietnamese (vi) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
3873
3874
3875
3876
3877
3878
3879
3880
+2
View File
@@ -4832,6 +4832,8 @@ lighting Light 光源
Horizontal Guide 水平参考线
Move Guide to Layer 将参考线移至图层
Linux Display Server Linux 显示服务器
X11 (XWayland) When a Pen Is Attached 连接手写笔时使用 X11 (XWayland)
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. 如果数位板的笔无法绘画,请选择一个 X11 (XWayland) 选项。
X11 (XWayland) X11 (XWayland)
Ruler Origin 标尺原点
Reset Ruler Origin 重置标尺原点
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -3909,6 +3909,8 @@ lighting Light 光源
Horizontal Guide 水平參考線
Move Guide to Layer 將參考線移至圖層
Linux Display Server Linux 顯示伺服器
X11 (XWayland) When a Pen Is Attached 連接觸控筆時使用 X11 (XWayland)
If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option. 如果繪圖板的筆無法繪圖,請選擇一個 X11 (XWayland) 選項。
X11 (XWayland) X11 (XWayland)
Ruler Origin 尺標原點
Reset Ruler Origin 重設尺標原點
Can't render this file because it contains an unexpected character in line 6 and column 47.
+42
View File
@@ -324,6 +324,14 @@ fn category_fields(ui: &mut egui::Ui, d: &mut Dialog, cat: &str) {
// The window's graphics device and display server are chosen once, when the app starts.
ui.label(egui::RichText::new(tl!("Applies the next time VectorCraft starts.")).color(t.text_dim).size(11.0));
}
if sp.key == "linuxDisplayServer" {
// Many Wayland desktops give a drawing tablet's pen to the window only through XWayland (#2028).
ui.label(
egui::RichText::new(tl!("If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option."))
.color(t.text_dim)
.size(11.0),
);
}
}
PrefKind::Color => {
labeled(ui, sp.label, |ui| {
@@ -614,6 +622,40 @@ mod tests {
assert_eq!(saved["engine_prefs"]["systemTitleBar"], json!(true));
}
/// Performance › Linux Display Server (#2028): on Linux it shows the drawing-tablet note, and
/// X11 (XWayland) When a Pen Is Attached is applied by OK and saved under the key the desktop
/// app reads before the window opens.
#[test]
fn linux_display_server_offers_the_pen_choice() {
fn texts(s: &egui::Shape, out: &mut Vec<String>) {
match s {
egui::Shape::Text(t) => out.push(t.galley.text().to_string()),
egui::Shape::Vec(v) => v.iter().for_each(|s| texts(s, out)),
_ => {}
}
}
let mut a = app();
open(&mut a, Some("Performance"));
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
// The window sizes itself on the first frame and draws on the second.
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| show(&mut a, ui.ctx()));
out.textures_delta.clear();
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| show(&mut a, ui.ctx()));
out.textures_delta.clear();
let mut shown = vec![];
out.shapes.iter().for_each(|c| texts(&c.shape, &mut shown));
let linux = cfg!(target_os = "linux");
assert_eq!(shown.iter().any(|t| t.starts_with("Linux Display Server")), linux, "{shown:?}");
let note = "If a drawing tablet's pen doesn't draw, choose an X11 (XWayland) option.";
assert_eq!(shown.iter().any(|t| t == note), linux, "{shown:?}");
a.ui.dialog.as_mut().unwrap().fields.insert("linuxDisplayServer".into(), json!("x11WithPen"));
confirm(&mut a).unwrap();
assert_eq!(a.session.prefs.linux_display_server, "x11WithPen");
let saved: Value = serde_json::from_slice(&serde_json::to_vec(&a.ui).unwrap()).unwrap();
assert_eq!(saved["engine_prefs"]["linuxDisplayServer"], json!("x11WithPen"));
}
/// #1866: the category shown is highlighted in the list while the pointer is elsewhere.
#[test]
fn the_category_shown_is_highlighted() {
+1 -1
View File
@@ -110,7 +110,7 @@ The window runs natively on Wayland (eframe's `wayland` feature) and on X11. On
- **Clipboard owners that never answer (#510):** reading the clipboard waits on the app that owns it: arboard gives up on an X11 owner after 4 s, and a Wayland read waits for the owner to send its data. So on Linux the check that enables the Paste menu items runs on a background thread with a second clipboard handle (`crates/ui-egui/src/clipboard_probe.rs`, installed as `Services::clipboard_probe`), only while nothing is copied in the app and a document is open, and the window keeps drawing. A paste itself still reads on the UI thread, so pasting from such an owner waits for it once. Windows and macOS keep the check on the UI thread: Windows only asks which formats the clipboard holds, macOS asks the pasteboard server.
- **Copied files:** files copied in a file manager (`text/uri-list`; `CF_HDROP` on Windows, file URLs on macOS) paste as the first one that is art (SVG, PDF, EPS, EMF/WMF or a bitmap; a `.ai` or `.ait` file as the PDF or PostScript it holds), before the clipboard's other formats (which include the files' paths as text).
- **Dropping files on the window does not work under Wayland:** winit 0.30, which eframe 0.36 runs on, has no Wayland drag and drop ([winit#1881](https://github.com/rust-windowing/winit/issues/1881), added in winit 0.31; [egui#1563](https://github.com/emilk/egui/issues/1563)), so no `dropped_files` arrive. Until eframe moves to winit 0.31: copy the files in the file manager and paste them, use File › Place or Open, or open the window through XWayland, where drops work: set **Preferences › Performance › Linux Display Server** to **X11 (XWayland)** and restart (#1151), or start the app with `WAYLAND_DISPLAY= vectorcraft`. The preference (`linuxDisplayServer`, read at startup in `apps/vectorcraft/src/main.rs`, shown on Linux only) has winit open the window on X11 (`EventLoopBuilderExtX11::with_x11`) when the session is Wayland, `DISPLAY` is set and the X11 libraries winit loads are installed (`apps/vectorcraft/src/xwayland.rs` reads `ldconfig -p` and the usual library directories); otherwise the window opens on Wayland and the log says why. Unlike clearing `WAYLAND_DISPLAY`, it leaves the clipboard on the compositor's own.
- **Pens and pen displays under Wayland (#491):** winit 0.30 doesn't bind the Wayland tablet protocol (`zwp_tablet_v2`; tablet input arrives in winit 0.31, [winit#4318](https://github.com/rust-windowing/winit/pull/4318), [egui#7731](https://github.com/emilk/egui/pull/7731)). Since Plasma 6.3, KWin no longer turns tablet input into pointer input for such apps ([kwin!6336](https://invent.kde.org/plasma/kwin/-/merge_requests/6336)): the pen moves the compositor's cursor over the window, but the app gets no motion or presses and shows none of its own cursors. Until eframe moves to winit 0.31, either run the app under XWayland, which supports the tablet protocol and hands the pen to X11 apps as a pointer (Preferences › Performance › Linux Display Server › X11 (XWayland), `WAYLAND_DISPLAY= vectorcraft`, or `env -u WAYLAND_DISPLAY vectorcraft`; for a desktop launcher, put `env WAYLAND_DISPLAY=` before the command in its `Exec=` line), or turn KWin's deprecated emulation back on for the whole session by setting `KWIN_WAYLAND_EMULATE_TABLET=1` in KWin's environment (for example in a file under `~/.config/environment.d/` or in `/etc/environment`) and logging in again. The pen then works as a mouse, without pressure: winit 0.30 reads pen pressure on Windows only (Windows Ink, which reaches the Liquify tools' Use Pressure Pen). Other compositors that don't emulate a pointer for tablets behave the same way.
- **Pens and pen displays under Wayland (#491, #2028):** winit 0.30 doesn't bind the Wayland tablet protocol (`zwp_tablet_v2`; tablet input arrives in winit 0.31, [winit#4318](https://github.com/rust-windowing/winit/pull/4318), [egui#7731](https://github.com/emilk/egui/pull/7731)). Since Plasma 6.3, KWin no longer turns tablet input into pointer input for such apps ([kwin!6336](https://invent.kde.org/plasma/kwin/-/merge_requests/6336), commit [25c65abd](https://invent.kde.org/plasma/kwin/-/commit/25c65abddd95eb87fb892a8594faccbf509ff62f)), and GNOME's Mutter does not turn it into pointer input either (that commit's message): on KWin the pen moves the compositor's cursor over the window, but the app gets no motion or presses and shows none of its own cursors. Until eframe moves to winit 0.31, either run the app under XWayland, which supports the tablet protocol and hands the pen to X11 apps as a pointer (Preferences › Performance › Linux Display Server › X11 (XWayland); X11 (XWayland) When a Pen Is Attached (`x11WithPen`), which opens the window through XWayland only when `/proc/bus/input/devices` lists a pen as the app starts, a device with the `BTN_TOOL_PEN` key and the `ABS_X` and `ABS_Y` axes, and logs `pen found: opening the window through XWayland`; `WAYLAND_DISPLAY= vectorcraft`, or `env -u WAYLAND_DISPLAY vectorcraft`; for a desktop launcher, put `env WAYLAND_DISPLAY=` before the command in its `Exec=` line), or turn KWin's deprecated emulation back on for the whole session by setting `KWIN_WAYLAND_EMULATE_TABLET=1` in KWin's environment (for example in a file under `~/.config/environment.d/` or in `/etc/environment`) and logging in again. The pen then works as a mouse, without pressure: winit 0.30 reads pen pressure on Windows only (Windows Ink, which reaches the Liquify tools' Use Pressure Pen). Other compositors that don't emulate a pointer for tablets behave the same way.
## Fonts: craft-fonts (optional build input)
+1 -1
View File
@@ -19,7 +19,7 @@ on huge documents. Pen input is pressure-only and Windows-first.
| Multithreaded rendering off the UI thread | partial | yes | yes | yes | single-threaded | beyond Illustrator |
| Hybrid-graphics GPU choice | automatic | power-saving GPU by default, Preferences › Graphics Processor | same; DX12 then OpenGL; no Vulkan unless asked (#545, #806) | the desktop's GPU | browser | done |
| Restart on another GPU when the first fails to present | — | yes (#502, #964) | yes | yes | restart on lost device (#369) | done |
| Pen pressure | yes (Wacom, Windows Ink, macOS tablet events) | **no** (winit reports none, #852) | yes (pointer force via `WM_POINTER`) | X11 only via XWayland (Preferences › Performance › Linux Display Server, #1151); none on Wayland (#491, #764) | browser pointer events, with coalesced events; Apple Pencil on iPad (hover, palm rejection: fingers navigate after a pen contact until reload) | partial: 4–8 h for macOS and Wayland (winit 0.31) |
| Pen pressure | yes (Wacom, Windows Ink, macOS tablet events) | **no** (winit reports none, #852) | yes (pointer force via `WM_POINTER`) | **no** (winit reports none on X11 or Wayland); through XWayland (Preferences › Performance › Linux Display Server, #1151, #2028) the pen works as a mouse, and on Wayland it may not reach the window (#491, #764) | browser pointer events, with coalesced events; Apple Pencil on iPad (hover, palm rejection: fingers navigate after a pen contact until reload) | partial: 4–8 h for macOS and Wayland (winit 0.31) |
| Pen tilt, bearing, barrel rotation (6D Art Pen) | yes, drives brushes | no | no | no | no | missing: 4–8 h after the input layer (#372) |
| Pen eraser end | yes | no | no | no | no | missing |
| Touch gestures (pinch zoom, two-finger pan, rotate view, Touch workspace) | yes (Windows touch, Surface) | trackpad pinch, scroll and rotate | pinch, pan; two-finger tap undo, three-finger redo (#585) | same where winit reports touch | eframe until the first pen contact, then Pointer Events (pen and fingers told apart); long press opens the context menu | partial: touch workspace and touch-screen rotation missing, 3–5 h |
+1
View File
@@ -740,6 +740,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
The Layers panel's Delete Selection, Duplicate Selection, Collect in New Layer, Merge Selected, Flatten Artwork, Release to Layers (Sequence) and row drags also take time in proportion to the size of the document, and so do Cut Artboard and Duplicate Artboard with their art and each move of an Alt-drag of one artboard with the Artboard tool while Move/Copy Artwork with Artboard is on (#1429).
In Outline view the renderer draws paths and compound paths from the geometry it keeps for them between frames, and it culls the members of clip groups by the bounds it keeps for them. Layers with Preview off and the outlines of Image Trace objects are drawn the same way.
- **Graphics processor:** the canvas is rasterized on the CPU and the GPU only composites it, so any GPU that can show the window will do. Preferences › Performance › Graphics Processor (`gpuPreference`) is Automatic by default: the power-saving GPU on Windows and macOS (the integrated one on hybrid-graphics laptops, where presenting from the discrete GPU made the window flicker, #306), and the GPU the desktop runs on under Linux, where KWin on an NVIDIA GPU refused frames from a Ryzen's integrated GPU and 0.5.0 crashed at startup (#502). Power Saving and High Performance pick a kind of GPU; `WGPU_POWER_PREF`, `WGPU_ADAPTER_NAME` and `WGPU_BACKEND` override them. The app picks among the adapters that can present to the window (native backends before OpenGL, hardware before software, and on Windows a GPU's DX12 adapter before its Vulkan one: Intel's Vulkan driver made the whole window flicker black, #545), and if the window fails on one while starting up it starts again without it instead of crashing; after a start that a graphics driver crashed before its first frame, the next starts leave that adapter out, or list OpenGL's adapters alone when the crash came while the adapters were being listed (#2243; verified by unit tests, not on the reporter's Intel UHD Graphics 605); Help › About, `ui.inspect` and the log name the adapter in use. When the browser drops the page's WebGPU device or WebGL context, the web build writes recovery copies, starts again on new graphics with the same app and uploads its textures again; if it can't, it removes the stale canvas and says to reload, which offers the copies back. The desktop app writes the copies and says to restart (#369). On Windows and Linux the tools' cursors are OS cursors (bitmaps rasterized once per display scale), which the system moves itself: painted into the window, they trailed the pointer by the frames waiting to be shown, and the window now queues one frame instead of two (#444). macOS and the web still paint them (macOS sizes cursor bitmaps in points, eframe has no bitmap cursors on the web). Tool states have cursors of their own as the public manual describes them: Direct Selection shows a hollow or filled square on its white arrow over an anchor (path unselected or selected) and the Reshape Segment cursor over a curved segment, as do the Anchor Point tool and the Pen with Alt (whose caret it lends); Group Selection, and Direct Selection with Alt, show a plus; a drag shows the filled arrow, hollow while it snaps to a point or a guide, and an Alt-drag copy a double arrowhead; Curvature has its own pen and Free Transform's Cmd/Ctrl side-shear two arrows passing along the side. Lasso, Magic Wand, the freehand tools, Shaper, Live Paint Bucket, Measure, Print Tiling, the symbolism tools, vertical type, Scissors, Mesh and Puppet Warp return cursors of their own, which borrow the crosshair, the I-beam, the Pen's add cursor or not-allowed until they're drawn; there's no busy cursor yet. `crates/tools/src/cursors.toml` lists every cursor, when it shows and its hotspot, for whoever draws them. Pens and touch drive the tools as the mouse does, and a tap pressed and lifted within one frame clicks where it tapped (#491); pen pressure reaches the Liquify tools' Use Pressure Pen on Windows, while winit 0.30 reports none on macOS or Linux and egui none on the web. On Linux, see `docs/development.md` for pens under Wayland.
- **Linux display server for pens (part of #2028):** Preferences › Performance › Linux Display Server has X11 (XWayland) When a Pen Is Attached (`x11WithPen`, off by default), which opens the window through XWayland on a Wayland session only when a pen is attached as the app starts. Under XWayland a drawing tablet's pen draws as a mouse. Below the choice, the Performance page tells users whose tablet pen doesn't draw to choose an X11 (XWayland) option.
- **Control transport:** idle sockets expire after 5 minutes, partial lines after an absolute 30 seconds from their first byte, and reply writes after 60 seconds (#1726). Real TCP regressions cover stalled peers and slot recovery; command execution has its separate existing timeout. MCP limits: 16 MiB lines, 64-message batches, 256-step `command_batch` with 8 MiB of kept results and 256 MiB control replies (#1764, fixes #1727). MCP requires the JSON-RPC 2.0 envelope and matches control replies by id, and an app refusal without an id reports the app's reason (#1768, fixes #1728). The MCP server reconnects to the app before a call when the connection was closed or holds stray bytes, after an app restart or an idle expiry (#1739).
- **MCP vertical type:** `add_text {vertical: true}` forwards to `text.create` or `text.createInPath`. Regression coverage checks point type, rectangular area type, type in a path and type on a path with true, false and omitted direction. This exposes an existing engine option; it does not change glyph orientation, CJK composition or the type/automation scores.
- **Malformed MCP replies:** invalid JSON from the app drops the connection, reports that the operation may have completed and leaves the next call to reconnect; it never replays the edit (#1761). TCP regressions cover malformed and uncorrelated replies, connection closure and a subsequent inspection. Envelope regressions also verify zero backend calls and unchanged initialization state for invalid versions.