mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
file chooser dialog events implementation.
This commit is contained in:
parent
22d31e871c
commit
d4e0287eb5
@ -97,7 +97,7 @@ func (h *RoomHandler) uploadDialogPost(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
defer r.MultipartForm.RemoveAll()
|
||||
|
||||
if !h.desktop.IsFileChooserDialogOpen() {
|
||||
if !h.desktop.IsFileChooserDialogOpened() {
|
||||
utils.HttpBadRequest(w, "Open file chooser dialog first.")
|
||||
return
|
||||
}
|
||||
@ -149,15 +149,11 @@ func (h *RoomHandler) uploadDialogPost(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (h *RoomHandler) uploadDialogClose(w http.ResponseWriter, r *http.Request) {
|
||||
if !h.desktop.IsFileChooserDialogOpen() {
|
||||
if !h.desktop.IsFileChooserDialogOpened() {
|
||||
utils.HttpBadRequest(w, "File chooser dialog is not open.")
|
||||
return
|
||||
}
|
||||
|
||||
if !h.desktop.CloseFileChooserDialog() {
|
||||
utils.HttpInternalServerError(w, "Unable to close file chooser dialog.")
|
||||
return
|
||||
}
|
||||
|
||||
h.desktop.CloseFileChooserDialog()
|
||||
utils.HttpSuccess(w)
|
||||
}
|
||||
|
@ -1,52 +1,9 @@
|
||||
package desktop
|
||||
|
||||
import (
|
||||
"time"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
var (
|
||||
file_chooser_dialog_open = false
|
||||
)
|
||||
|
||||
func (manager *DesktopManagerCtx) fileChooserDialogStart() {
|
||||
if manager.IsFileChooserDialogOpen() {
|
||||
manager.CloseFileChooserDialog()
|
||||
}
|
||||
|
||||
manager.OnWindowCreated(func(window uint32, name string, role string) {
|
||||
if role != "GtkFileChooserDialog" {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement, call event.
|
||||
file_chooser_dialog_open = true
|
||||
|
||||
manager.logger.Debug().
|
||||
Uint32("window", window).
|
||||
Msg("GtkFileChooserDialog has been opened")
|
||||
})
|
||||
|
||||
manager.OnWindowConfigured(func(window uint32, name string, role string) {
|
||||
if role != "GtkFileChooserDialog" {
|
||||
return
|
||||
}
|
||||
|
||||
go func(){
|
||||
// TOOD: Refactor.
|
||||
manager.PutWindowBelow(window)
|
||||
|
||||
// Because first dialog is not put properly to background
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
manager.PutWindowBelow(window)
|
||||
}()
|
||||
|
||||
manager.logger.Debug().
|
||||
Uint32("window", window).
|
||||
Msg("GtkFileChooserDialog has been put below main window")
|
||||
})
|
||||
}
|
||||
|
||||
func (manager *DesktopManagerCtx) HandleFileChooserDialog(uri string) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
@ -54,7 +11,7 @@ func (manager *DesktopManagerCtx) HandleFileChooserDialog(uri string) error {
|
||||
// TOOD: Use native API.
|
||||
cmd := exec.Command(
|
||||
"xdotool",
|
||||
"search", "--name", "Open", "windowfocus",
|
||||
"search", "--name", "Open File", "windowfocus",
|
||||
"sleep", "0.2",
|
||||
"key", "--clearmodifiers", "ctrl+l",
|
||||
"type", "--args", "1", uri + "//",
|
||||
@ -66,43 +23,36 @@ func (manager *DesktopManagerCtx) HandleFileChooserDialog(uri string) error {
|
||||
"sleep", "0.3",
|
||||
)
|
||||
|
||||
// TODO: Implement, call event.
|
||||
file_chooser_dialog_open = false
|
||||
|
||||
_, err := cmd.Output()
|
||||
return err
|
||||
}
|
||||
|
||||
func (manager *DesktopManagerCtx) CloseFileChooserDialog() bool {
|
||||
func (manager *DesktopManagerCtx) CloseFileChooserDialog() {
|
||||
for i := 0; i < 5; i++ {
|
||||
if !manager.IsFileChooserDialogOpened() {
|
||||
return
|
||||
}
|
||||
|
||||
// TOOD: Use native API.
|
||||
mu.Lock()
|
||||
exec.Command(
|
||||
"xdotool",
|
||||
"search", "--name", "Open", "windowfocus",
|
||||
"search", "--name", "Open File", "windowfocus",
|
||||
"sleep", "0.2",
|
||||
"key", "--clearmodifiers", "alt+F4",
|
||||
).Output()
|
||||
mu.Unlock()
|
||||
|
||||
if !manager.IsFileChooserDialogOpen() {
|
||||
// TODO: Implement, call event.
|
||||
file_chooser_dialog_open = false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (manager *DesktopManagerCtx) IsFileChooserDialogOpen() bool {
|
||||
func (manager *DesktopManagerCtx) IsFileChooserDialogOpened() bool {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
// TOOD: Use native API.
|
||||
cmd := exec.Command(
|
||||
"xdotool",
|
||||
"search", "--name", "Open", "windowfocus",
|
||||
"search", "--name", "Open File",
|
||||
)
|
||||
|
||||
_, err := cmd.Output()
|
||||
|
@ -53,7 +53,8 @@ func (manager *DesktopManagerCtx) Start() {
|
||||
|
||||
go xevent.EventLoop(manager.display)
|
||||
|
||||
go manager.fileChooserDialogStart()
|
||||
// In case it was opened
|
||||
go manager.CloseFileChooserDialog()
|
||||
|
||||
manager.OnEventError(func(error_code uint8, message string, request_code uint8, minor_code uint8) {
|
||||
manager.logger.Warn().
|
||||
|
@ -12,12 +12,12 @@ func (manager *DesktopManagerCtx) OnClipboardUpdated(listener func()) {
|
||||
xevent.OnClipboardUpdated(listener)
|
||||
}
|
||||
|
||||
func (manager *DesktopManagerCtx) OnWindowCreated(listener func(window uint32, name string, role string)) {
|
||||
xevent.OnWindowCreated(listener)
|
||||
func (manager *DesktopManagerCtx) OnFileChooserDialogOpened(listener func()) {
|
||||
xevent.OnFileChooserDialogOpened(listener)
|
||||
}
|
||||
|
||||
func (manager *DesktopManagerCtx) OnWindowConfigured(listener func(window uint32, name string, role string)) {
|
||||
xevent.OnWindowConfigured(listener)
|
||||
func (manager *DesktopManagerCtx) OnFileChooserDialogClosed(listener func()) {
|
||||
xevent.OnFileChooserDialogClosed(listener)
|
||||
}
|
||||
|
||||
func (manager *DesktopManagerCtx) OnEventError(listener func(error_code uint8, message string, request_code uint8, minor_code uint8)) {
|
||||
|
@ -64,7 +64,7 @@ void XEventLoop(char *name) {
|
||||
XTextProperty role;
|
||||
XGetTextProperty(display, window, &role, WM_WINDOW_ROLE);
|
||||
|
||||
goXEventWindowCreated(window, name, role.value);
|
||||
goXEventCreateNotify(window, name, role.value);
|
||||
XFree(name);
|
||||
continue;
|
||||
}
|
||||
@ -79,11 +79,60 @@ void XEventLoop(char *name) {
|
||||
XTextProperty role;
|
||||
XGetTextProperty(display, window, &role, WM_WINDOW_ROLE);
|
||||
|
||||
goXEventWindowConfigured(window, name, role.value);
|
||||
goXEventConfigureNotify(display, window, name, role.value);
|
||||
XFree(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// UnmapNotify
|
||||
if (event.type == UnmapNotify) {
|
||||
Window window = event.xunmap.window;
|
||||
goXEventUnmapNotify(window);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
XCloseDisplay(display);
|
||||
}
|
||||
|
||||
void XFileChooserHide(Display *display, Window window) {
|
||||
Window root = RootWindow(display, 0);
|
||||
|
||||
// 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
|
||||
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] = 1;
|
||||
clientMessageEvent.data.l[1] = XInternAtom(display, "_NET_WM_STATE_BELOW", 0);
|
||||
clientMessageEvent.data.l[3] = 1;
|
||||
|
||||
XSendEvent(display, root, 0, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&clientMessageEvent);
|
||||
}
|
||||
|
@ -8,12 +8,14 @@ package xevent
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/kataras/go-events"
|
||||
)
|
||||
|
||||
var emmiter events.EventEmmiter
|
||||
var file_chooser_dialog_window uint32 = 0
|
||||
|
||||
func init() {
|
||||
emmiter = events.New()
|
||||
@ -38,15 +40,15 @@ func OnClipboardUpdated(listener func()) {
|
||||
})
|
||||
}
|
||||
|
||||
func OnWindowCreated(listener func(window uint32, name string, role string)) {
|
||||
emmiter.On("window-created", func(payload ...interface{}) {
|
||||
listener(payload[0].(uint32), payload[1].(string), payload[2].(string))
|
||||
func OnFileChooserDialogOpened(listener func()) {
|
||||
emmiter.On("file-chooser-dialog-opened", func(payload ...interface{}) {
|
||||
listener()
|
||||
})
|
||||
}
|
||||
|
||||
func OnWindowConfigured(listener func(window uint32, name string, role string)) {
|
||||
emmiter.On("window-configured", func(payload ...interface{}) {
|
||||
listener(payload[0].(uint32), payload[1].(string), payload[2].(string))
|
||||
func OnFileChooserDialogClosed(listener func()) {
|
||||
emmiter.On("file-chooser-dialog-closed", func(payload ...interface{}) {
|
||||
listener()
|
||||
})
|
||||
}
|
||||
|
||||
@ -66,14 +68,38 @@ func goXEventClipboardUpdated() {
|
||||
emmiter.Emit("clipboard-updated")
|
||||
}
|
||||
|
||||
//export goXEventWindowCreated
|
||||
func goXEventWindowCreated(window C.Window, name *C.char, role *C.char) {
|
||||
emmiter.Emit("window-created", uint32(window), C.GoString(name), C.GoString(role))
|
||||
//export goXEventCreateNotify
|
||||
func goXEventCreateNotify(window C.Window, nameUnsafe *C.char, roleUnsafe *C.char) {
|
||||
role := C.GoString(roleUnsafe)
|
||||
if role != "GtkFileChooserDialog" {
|
||||
return
|
||||
}
|
||||
|
||||
//export goXEventWindowConfigured
|
||||
func goXEventWindowConfigured(window C.Window, name *C.char, role *C.char) {
|
||||
emmiter.Emit("window-configured", uint32(window), C.GoString(name), C.GoString(role))
|
||||
file_chooser_dialog_window = uint32(window)
|
||||
emmiter.Emit("file-chooser-dialog-opened")
|
||||
}
|
||||
|
||||
//export goXEventConfigureNotify
|
||||
func goXEventConfigureNotify(display *C.Display, window C.Window, nameUnsafe *C.char, roleUnsafe *C.char) {
|
||||
role := C.GoString(roleUnsafe)
|
||||
if role != "GtkFileChooserDialog" {
|
||||
return
|
||||
}
|
||||
|
||||
C.XFileChooserHide(display, window)
|
||||
|
||||
// Because first dialog is not put properly to background
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
C.XFileChooserHide(display, window)
|
||||
}
|
||||
|
||||
//export goXEventUnmapNotify
|
||||
func goXEventUnmapNotify(window C.Window) {
|
||||
if uint32(window) != file_chooser_dialog_window {
|
||||
return
|
||||
}
|
||||
|
||||
emmiter.Emit("file-chooser-dialog-closed")
|
||||
}
|
||||
|
||||
//export goXEventError
|
||||
|
@ -5,13 +5,17 @@
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern void goXEventCursorChanged(XFixesCursorNotifyEvent event);
|
||||
extern void goXEventClipboardUpdated();
|
||||
extern void goXEventWindowCreated(Window window, char *name, char *role);
|
||||
extern void goXEventWindowConfigured(Window window, char *name, char *role);
|
||||
extern void goXEventCreateNotify(Window window, char *name, char *role);
|
||||
extern void goXEventConfigureNotify(Display *display, Window window, char *name, char *role);
|
||||
extern void goXEventUnmapNotify(Window window);
|
||||
extern void goXEventError(XErrorEvent *event, char *message);
|
||||
extern int goXEventActive();
|
||||
|
||||
static int XEventError(Display *display, XErrorEvent *event);
|
||||
void XEventLoop(char *display);
|
||||
|
||||
void XFileChooserHide(Display *display, Window window);
|
||||
|
@ -113,7 +113,3 @@ func (manager *DesktopManagerCtx) GetKeyboardModifiers() types.KeyboardModifiers
|
||||
func (manager *DesktopManagerCtx) GetCursorImage() *types.CursorImage {
|
||||
return xorg.GetCursorImage()
|
||||
}
|
||||
|
||||
func (manager *DesktopManagerCtx) PutWindowBelow(window uint32) {
|
||||
xorg.PutWindowBelow(window)
|
||||
}
|
||||
|
@ -136,46 +136,3 @@ XFixesCursorImage *XGetCursorImage(void) {
|
||||
Display *display = getXDisplay();
|
||||
return XFixesGetCursorImage(display);
|
||||
}
|
||||
|
||||
void XPutWindowBelow(Window window) {
|
||||
Display *display = getXDisplay();
|
||||
Window root = RootWindow(display, 0);
|
||||
|
||||
// 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
|
||||
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] = 1;
|
||||
clientMessageEvent.data.l[1] = XInternAtom(display, "_NET_WM_STATE_BELOW", 0);
|
||||
clientMessageEvent.data.l[3] = 1;
|
||||
|
||||
XSendEvent(display, root, 0, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&clientMessageEvent);
|
||||
}
|
||||
|
@ -244,13 +244,6 @@ func GetCursorImage() *types.CursorImage {
|
||||
}
|
||||
}
|
||||
|
||||
func PutWindowBelow(window uint32) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
C.XPutWindowBelow(C.Window(window))
|
||||
}
|
||||
|
||||
//export goCreateScreenSize
|
||||
func goCreateScreenSize(index C.int, width C.int, height C.int, mwidth C.int, mheight C.int) {
|
||||
ScreenConfigurations[int(index)] = types.ScreenConfiguration{
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <X11/extensions/XTest.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern void goCreateScreenSize(int index, int width, int height, int mwidth, int mheight);
|
||||
extern void goSetScreenRates(int index, int rate_index, short rate);
|
||||
@ -28,5 +27,3 @@ short XGetScreenRate();
|
||||
void XSetKeyboardModifier(int mod, int on);
|
||||
char XGetKeyboardModifiers();
|
||||
XFixesCursorImage *XGetCursorImage(void);
|
||||
|
||||
void XPutWindowBelow(Window window);
|
||||
|
@ -57,8 +57,8 @@ type DesktopManager interface {
|
||||
// xevent
|
||||
OnCursorChanged(listener func(serial uint64))
|
||||
OnClipboardUpdated(listener func())
|
||||
OnWindowCreated(listener func(window uint32, name string, role string))
|
||||
OnWindowConfigured(listener func(window uint32, name string, role string))
|
||||
OnFileChooserDialogOpened(listener func())
|
||||
OnFileChooserDialogClosed(listener func())
|
||||
OnEventError(listener func(error_code uint8, message string, request_code uint8, minor_code uint8))
|
||||
|
||||
// clipboard
|
||||
@ -70,6 +70,6 @@ type DesktopManager interface {
|
||||
|
||||
// filechooser
|
||||
HandleFileChooserDialog(uri string) error
|
||||
CloseFileChooserDialog() bool
|
||||
IsFileChooserDialogOpen() bool
|
||||
CloseFileChooserDialog()
|
||||
IsFileChooserDialogOpened() bool
|
||||
}
|
||||
|
@ -134,13 +134,16 @@ func (ws *WebSocketManagerCtx) Start() {
|
||||
}
|
||||
})
|
||||
|
||||
ws.desktop.OnWindowCreated(func(window uint32, name string, role string) {
|
||||
ws.desktop.OnFileChooserDialogOpened(func() {
|
||||
// TODO: Implement.
|
||||
ws.logger.Info().
|
||||
Uint32("window", window).
|
||||
Str("name", name).
|
||||
Str("role", role).
|
||||
Msg("created new window")
|
||||
Msg("FileChooserDialog opened")
|
||||
})
|
||||
|
||||
ws.desktop.OnFileChooserDialogClosed(func() {
|
||||
// TODO: Implement.
|
||||
ws.logger.Info().
|
||||
Msg("FileChooserDialog closed")
|
||||
})
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user