Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions decorated-window-tao/api/decorated-window-tao.api
Original file line number Diff line number Diff line change
Expand Up @@ -1261,6 +1261,8 @@ public final class dev/nucleusframework/window/tao/TaoMonitors {

public final class dev/nucleusframework/window/tao/TaoMouseButton {
public static final field $stable I
public static final field BACK I
public static final field FORWARD I
public static final field INSTANCE Ldev/nucleusframework/window/tao/TaoMouseButton;
public static final field LEFT I
public static final field MIDDLE I
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,7 @@ public object TaoMouseButton {
public const val LEFT: Int = 0
public const val RIGHT: Int = 1
public const val MIDDLE: Int = 2
public const val OTHER: Int = 3
public const val BACK: Int = 3
public const val FORWARD: Int = 4
public const val OTHER: Int = 5
}
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,8 @@ internal class TaoPopupSceneLayerLinux(
when (code) {
TaoMouseButton.RIGHT -> PointerButton.Secondary
TaoMouseButton.MIDDLE -> PointerButton.Tertiary
TaoMouseButton.BACK -> PointerButton.Back
TaoMouseButton.FORWARD -> PointerButton.Forward
else -> PointerButton.Primary
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ internal abstract class AbstractTaoComposeSceneHost {
TaoMouseButton.LEFT -> PointerButton.Primary
TaoMouseButton.RIGHT -> PointerButton.Secondary
TaoMouseButton.MIDDLE -> PointerButton.Tertiary
TaoMouseButton.BACK -> PointerButton.Back
TaoMouseButton.FORWARD -> PointerButton.Forward
else -> PointerButton.Primary
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -988,16 +988,18 @@ typedef struct {
double delta_y;
} gdk_event_scroll_t;

/* Map GTK's native button code (1 = LEFT, 2 = MIDDLE, 3 = RIGHT) to
* Tao's AWT-style encoding (`TaoMouseButton.LEFT = 0`, `RIGHT = 1`,
* `MIDDLE = 2`). Anything else stays a passthrough β€” Compose's
* `mapButton` falls back to `Primary` for unknown codes. */
/* Map GTK's native button code (1 = LEFT, 2 = MIDDLE, 3 = RIGHT,
* 8 = BACK, 9 = FORWARD) to Tao's AWT-style encoding
* (`TaoMouseButton.LEFT = 0` … `FORWARD = 4`, `OTHER = 5`) β€” the same
* codes `events.rs` `mouse_button_code` sends for the main surface. */
static int gtk_button_to_tao(unsigned int gtk_button) {
switch (gtk_button) {
case 1: return 0; /* LEFT */
case 2: return 2; /* MIDDLE */
case 3: return 1; /* RIGHT */
default: return (int) gtk_button;
case 8: return 3; /* BACK */
case 9: return 4; /* FORWARD */
default: return 5; /* OTHER */
}
}

Expand Down
22 changes: 21 additions & 1 deletion decorated-window-tao/src/main/native/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,25 @@ pub(crate) const TOUCH_FORCE_UNKNOWN: jint = -1;
pub(crate) const MOUSE_BUTTON_LEFT: jint = 0;
pub(crate) const MOUSE_BUTTON_RIGHT: jint = 1;
pub(crate) const MOUSE_BUTTON_MIDDLE: jint = 2;
pub(crate) const MOUSE_BUTTON_OTHER: jint = 3;
pub(crate) const MOUSE_BUTTON_BACK: jint = 3;
pub(crate) const MOUSE_BUTTON_FORWARD: jint = 4;
pub(crate) const MOUSE_BUTTON_OTHER: jint = 5;

// Raw `MouseButton::Other(n)` numbers tao reports for the back / forward side
// buttons: `XBUTTON1` / `XBUTTON2` on Windows, X11/GDK buttons 8 / 9 on Linux,
// `NSEvent.buttonNumber` 3 / 4 on macOS.
#[cfg(target_os = "windows")]
const OTHER_BACK: u16 = 1;
#[cfg(target_os = "windows")]
const OTHER_FORWARD: u16 = 2;
#[cfg(target_os = "macos")]
const OTHER_BACK: u16 = 3;
#[cfg(target_os = "macos")]
const OTHER_FORWARD: u16 = 4;
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
const OTHER_BACK: u16 = 8;
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
const OTHER_FORWARD: u16 = 9;

// ── User events posted from JNI calls into the event loop ─────────────────

Expand Down Expand Up @@ -682,6 +700,8 @@ pub(crate) fn mouse_button_code(b: MouseButton) -> jint {
MouseButton::Left => MOUSE_BUTTON_LEFT,
MouseButton::Right => MOUSE_BUTTON_RIGHT,
MouseButton::Middle => MOUSE_BUTTON_MIDDLE,
MouseButton::Other(OTHER_BACK) => MOUSE_BUTTON_BACK,
MouseButton::Other(OTHER_FORWARD) => MOUSE_BUTTON_FORWARD,
_ => MOUSE_BUTTON_OTHER,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1152,14 +1152,24 @@ extern "C" fn right_mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_click(this, event, MouseButton::Right, ElementState::Released);
}

// Nucleus: `otherMouseDown:` fires for every button past the right one, so
// read `buttonNumber` instead of assuming Middle β€” 3/4 are the back/forward
// side buttons, surfaced as `Other(3)` / `Other(4)`.
fn other_mouse_button(event: &NSEvent) -> MouseButton {
match event.buttonNumber() {
2 => MouseButton::Middle,
n => MouseButton::Other(n as u16),
}
}

extern "C" fn other_mouse_down(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
mouse_click(this, event, MouseButton::Middle, ElementState::Pressed);
mouse_click(this, event, other_mouse_button(event), ElementState::Pressed);
}

extern "C" fn other_mouse_up(this: &NSView, _sel: Sel, event: &NSEvent) {
mouse_motion(this, event);
mouse_click(this, event, MouseButton::Middle, ElementState::Released);
mouse_click(this, event, other_mouse_button(event), ElementState::Released);
}

fn mouse_motion(this: &NSView, event: &NSEvent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package dev.nucleusframework.window.tao

import java.io.File
import java.lang.reflect.Modifier
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.fail

/**
* Mouse-button codes are written by hand on both sides of the JNI boundary:
* `events.rs` `MOUSE_BUTTON_*` and [TaoMouseButton]. A drift is silent β€” the
* Rust "other" code once shared its number with [TaoMouseButton.BACK], so
* every extra button reached Compose as Back β€” so compare them here.
*/
class TaoMouseButtonWireDriftTest {
@Test
fun `Rust MOUSE_BUTTON codes match TaoMouseButton`() {
val rust =
RUST_CODE
.findAll(eventsRs().readText())
.associate { it.groupValues[1] to it.groupValues[2].toInt() }
val kotlin =
TaoMouseButton::class.java.declaredFields
.filter { Modifier.isStatic(it.modifiers) && it.type == Integer.TYPE && it.name != "\$stable" }
.associate { it.name to it.getInt(null) }
assertEquals(kotlin, rust, "events.rs MOUSE_BUTTON_* vs TaoMouseButton")
}

private fun eventsRs(): File {
val relative = "src/main/native/src/events.rs"
// Module directory first (Gradle), then the repository root (IDE).
val candidates = listOf(File(relative), File("decorated-window-tao", relative))
return candidates.firstOrNull { it.isFile }
?: fail("cannot find $relative from ${File("").absolutePath} (tried ${candidates.map { it.path }})")
}

private companion object {
val RUST_CODE = Regex("""pub\(crate\) const MOUSE_BUTTON_(\w+): jint = (\d+);""")
}
}
Loading