mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
move server to server directory.
This commit is contained in:
192
server/pkg/xevent/xevent.c
Normal file
192
server/pkg/xevent/xevent.c
Normal file
@ -0,0 +1,192 @@
|
||||
#include "xevent.h"
|
||||
|
||||
static int XEventError(Display *display, XErrorEvent *event) {
|
||||
char message[100];
|
||||
|
||||
int error;
|
||||
error = XGetErrorText(display, event->error_code, message, sizeof(message));
|
||||
if (error) {
|
||||
goXEventError(event, "Could not get error message.");
|
||||
} else {
|
||||
goXEventError(event, message);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void XSetupErrorHandler() {
|
||||
XSetErrorHandler(XEventError);
|
||||
}
|
||||
|
||||
void XEventLoop(char *name) {
|
||||
Display *display = XOpenDisplay(name);
|
||||
Window root = DefaultRootWindow(display);
|
||||
|
||||
int xfixes_event_base, xfixes_error_base;
|
||||
if (!XFixesQueryExtension(display, &xfixes_event_base, &xfixes_error_base)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// save last size id for fullscreen bug fix
|
||||
SizeID last_size_id;
|
||||
|
||||
Atom WM_WINDOW_ROLE = XInternAtom(display, "WM_WINDOW_ROLE", 1);
|
||||
Atom XA_CLIPBOARD = XInternAtom(display, "CLIPBOARD", 0);
|
||||
XFixesSelectSelectionInput(display, root, XA_CLIPBOARD, XFixesSetSelectionOwnerNotifyMask);
|
||||
XFixesSelectCursorInput(display, root, XFixesDisplayCursorNotifyMask);
|
||||
XSelectInput(display, root, SubstructureNotifyMask);
|
||||
|
||||
XSync(display, 0);
|
||||
|
||||
while (goXEventActive()) {
|
||||
XEvent event;
|
||||
XNextEvent(display, &event);
|
||||
|
||||
// XFixesDisplayCursorNotify
|
||||
if (event.type == xfixes_event_base + 1) {
|
||||
XFixesCursorNotifyEvent notifyEvent = *((XFixesCursorNotifyEvent *) &event);
|
||||
if (notifyEvent.subtype == XFixesDisplayCursorNotify) {
|
||||
goXEventCursorChanged(notifyEvent);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// XFixesSelectionNotifyEvent
|
||||
if (event.type == xfixes_event_base + XFixesSelectionNotify) {
|
||||
XFixesSelectionNotifyEvent notifyEvent = *((XFixesSelectionNotifyEvent *) &event);
|
||||
if (notifyEvent.subtype == XFixesSetSelectionOwnerNotify && notifyEvent.selection == XA_CLIPBOARD) {
|
||||
goXEventClipboardUpdated();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigureNotify
|
||||
if (event.type == ConfigureNotify) {
|
||||
Window window = event.xconfigure.window;
|
||||
|
||||
char *name;
|
||||
XFetchName(display, window, &name);
|
||||
|
||||
XTextProperty role;
|
||||
XGetTextProperty(display, window, &role, WM_WINDOW_ROLE);
|
||||
|
||||
goXEventConfigureNotify(display, window, name, role.value);
|
||||
XFree(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// UnmapNotify
|
||||
if (event.type == UnmapNotify) {
|
||||
Window window = event.xunmap.window;
|
||||
goXEventUnmapNotify(window);
|
||||
continue;
|
||||
}
|
||||
|
||||
// ClientMessage
|
||||
if (event.type == ClientMessage) {
|
||||
Window window = event.xclient.window;
|
||||
|
||||
// check for net window manager state (fullscreen, maximized, etc)
|
||||
if (event.xclient.message_type == XInternAtom(display, "_NET_WM_STATE", 0)) {
|
||||
// see documentation in XWindowManagerStateEvent
|
||||
Atom action = event.xclient.data.l[0];
|
||||
Atom first = event.xclient.data.l[1];
|
||||
Atom second = event.xclient.data.l[2];
|
||||
|
||||
// check if user is entering or exiting fullscreen
|
||||
if (first == XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", 0)) {
|
||||
// get current size id
|
||||
Rotation current_rotation;
|
||||
XRRScreenConfiguration *conf = XRRGetScreenInfo(display, root);
|
||||
SizeID current_size_id = XRRConfigCurrentConfiguration(conf, ¤t_rotation);
|
||||
|
||||
// window just went fullscreen
|
||||
if (action == 1) {
|
||||
// save current size id
|
||||
last_size_id = current_size_id;
|
||||
continue;
|
||||
}
|
||||
|
||||
// window just exited fullscreen
|
||||
if (action == 0) {
|
||||
// if size id changed, that means user changed resolution while in fullscreen
|
||||
// there is a bug in some window managers that causes the window to not resize
|
||||
// if it was previously maximized, so we need to unmaximize it and maximize it again
|
||||
if (current_size_id != last_size_id) {
|
||||
// toggle maximized state twice
|
||||
XWindowManagerStateEvent(display, window, 2,
|
||||
XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", 0),
|
||||
XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", 0));
|
||||
XWindowManagerStateEvent(display, window, 2,
|
||||
XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", 0),
|
||||
XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for window manager change state (minimize, maximize, etc)
|
||||
if (event.xclient.message_type == XInternAtom(display, "WM_CHANGE_STATE", 0)) {
|
||||
int window_state = event.xclient.data.l[0];
|
||||
// NormalState - The client's top-level window is viewable.
|
||||
// IconicState - The client's top-level window is iconic (whatever that means for this window manager).
|
||||
// WithdrawnState - Neither the client's top-level window nor its icon is visible.
|
||||
goXEventWMChangeState(display, window, window_state);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
|
||||
static void XWindowManagerStateEvent(Display *display, Window window, ulong action, ulong first, ulong second) {
|
||||
Window root = DefaultRootWindow(display);
|
||||
|
||||
XClientMessageEvent clientMessageEvent;
|
||||
memset(&clientMessageEvent, 0, sizeof(clientMessageEvent));
|
||||
|
||||
// window = the respective client window
|
||||
// message_type = _NET_WM_STATE
|
||||
// format = 32
|
||||
// data.l[0] = the action, as listed below
|
||||
// _NET_WM_STATE_REMOVE 0 // remove/unset property
|
||||
// _NET_WM_STATE_ADD 1 // add/set property
|
||||
// _NET_WM_STATE_TOGGLE 2 // toggle property
|
||||
// data.l[1] = first property to alter
|
||||
// data.l[2] = second property to alter
|
||||
// data.l[3] = source indication
|
||||
// other data.l[] elements = 0
|
||||
|
||||
clientMessageEvent.type = ClientMessage;
|
||||
clientMessageEvent.window = window;
|
||||
clientMessageEvent.message_type = XInternAtom(display, "_NET_WM_STATE", 0);
|
||||
clientMessageEvent.format = 32;
|
||||
clientMessageEvent.data.l[0] = action;
|
||||
clientMessageEvent.data.l[1] = first;
|
||||
clientMessageEvent.data.l[2] = second;
|
||||
clientMessageEvent.data.l[3] = 1;
|
||||
|
||||
XSendEvent(display, root, 0, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&clientMessageEvent);
|
||||
XFlush(display);
|
||||
}
|
||||
|
||||
void XFileChooserHide(Display *display, Window window) {
|
||||
// The WM_TRANSIENT_FOR property is defined by the [ICCCM] for managed windows.
|
||||
// This specification extends the use of the property to override-redirect windows.
|
||||
// If an override-redirect is a pop-up on behalf of another window, then the Client
|
||||
// SHOULD set WM_TRANSIENT_FOR on the override-redirect to this other window.
|
||||
//
|
||||
// As an example, a Client should set WM_TRANSIENT_FOR on dropdown menus to the
|
||||
// toplevel application window that contains the menubar.
|
||||
|
||||
// Remove WM_TRANSIENT_FOR
|
||||
Atom WM_TRANSIENT_FOR = XInternAtom(display, "WM_TRANSIENT_FOR", 0);
|
||||
XDeleteProperty(display, window, WM_TRANSIENT_FOR);
|
||||
|
||||
// Add _NET_WM_STATE_BELOW
|
||||
XWindowManagerStateEvent(display, window, 1, // set property
|
||||
XInternAtom(display, "_NET_WM_STATE_BELOW", 0), 0);
|
||||
}
|
96
server/pkg/xevent/xevent.go
Normal file
96
server/pkg/xevent/xevent.go
Normal file
@ -0,0 +1,96 @@
|
||||
package xevent
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lX11 -lXfixes
|
||||
|
||||
#include "xevent.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/kataras/go-events"
|
||||
)
|
||||
|
||||
var Emmiter events.EventEmmiter
|
||||
var Unminimize bool = false
|
||||
var FileChooserDialog bool = false
|
||||
var fileChooserDialogWindow uint32 = 0
|
||||
|
||||
func init() {
|
||||
Emmiter = events.New()
|
||||
}
|
||||
|
||||
func SetupErrorHandler() {
|
||||
C.XSetupErrorHandler()
|
||||
}
|
||||
|
||||
func EventLoop(display string) {
|
||||
displayUnsafe := C.CString(display)
|
||||
defer C.free(unsafe.Pointer(displayUnsafe))
|
||||
|
||||
C.XEventLoop(displayUnsafe)
|
||||
}
|
||||
|
||||
//export goXEventCursorChanged
|
||||
func goXEventCursorChanged(event C.XFixesCursorNotifyEvent) {
|
||||
Emmiter.Emit("cursor-changed", uint64(event.cursor_serial))
|
||||
}
|
||||
|
||||
//export goXEventClipboardUpdated
|
||||
func goXEventClipboardUpdated() {
|
||||
Emmiter.Emit("clipboard-updated")
|
||||
}
|
||||
|
||||
//export goXEventConfigureNotify
|
||||
func goXEventConfigureNotify(display *C.Display, window C.Window, name *C.char, role *C.char) {
|
||||
if C.GoString(role) != "GtkFileChooserDialog" || !FileChooserDialog {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Refactor. Right now processing of this dialog relies on identifying
|
||||
// via its name. When that changes to role, this condition should be removed.
|
||||
if !strings.HasPrefix(C.GoString(name), "Open File") {
|
||||
return
|
||||
}
|
||||
|
||||
C.XFileChooserHide(display, window)
|
||||
|
||||
if fileChooserDialogWindow == 0 {
|
||||
fileChooserDialogWindow = uint32(window)
|
||||
Emmiter.Emit("file-chooser-dialog-opened")
|
||||
}
|
||||
}
|
||||
|
||||
//export goXEventUnmapNotify
|
||||
func goXEventUnmapNotify(window C.Window) {
|
||||
if uint32(window) != fileChooserDialogWindow || !FileChooserDialog {
|
||||
return
|
||||
}
|
||||
|
||||
fileChooserDialogWindow = 0
|
||||
Emmiter.Emit("file-chooser-dialog-closed")
|
||||
}
|
||||
|
||||
//export goXEventWMChangeState
|
||||
func goXEventWMChangeState(display *C.Display, window C.Window, window_state C.ulong) {
|
||||
// if we just realized that window is minimized and we want it to be unminimized
|
||||
if window_state != C.NormalState && Unminimize {
|
||||
// we want to unmap and map the window to force it to redraw
|
||||
C.XUnmapWindow(display, window)
|
||||
C.XMapWindow(display, window)
|
||||
C.XFlush(display)
|
||||
}
|
||||
}
|
||||
|
||||
//export goXEventError
|
||||
func goXEventError(event *C.XErrorEvent, message *C.char) {
|
||||
Emmiter.Emit("event-error", uint8(event.error_code), C.GoString(message), uint8(event.request_code), uint8(event.minor_code))
|
||||
}
|
||||
|
||||
//export goXEventActive
|
||||
func goXEventActive() C.int {
|
||||
return C.int(1)
|
||||
}
|
24
server/pkg/xevent/xevent.h
Normal file
24
server/pkg/xevent/xevent.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern void goXEventCursorChanged(XFixesCursorNotifyEvent event);
|
||||
extern void goXEventClipboardUpdated();
|
||||
extern void goXEventConfigureNotify(Display *display, Window window, char *name, char *role);
|
||||
extern void goXEventUnmapNotify(Window window);
|
||||
extern void goXEventWMChangeState(Display *display, Window window, ulong state);
|
||||
extern void goXEventError(XErrorEvent *event, char *message);
|
||||
extern int goXEventActive();
|
||||
|
||||
static int XEventError(Display *display, XErrorEvent *event);
|
||||
void XSetupErrorHandler();
|
||||
void XEventLoop(char *display);
|
||||
|
||||
static void XWindowManagerStateEvent(Display *display, Window window, ulong action, ulong first, ulong second);
|
||||
void XFileChooserHide(Display *display, Window window);
|
Reference in New Issue
Block a user