mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 05:00:31 +08:00
Support custom protocols
This commit is contained in:
parent
2d1b2c0947
commit
b4c669ff86
@ -35,6 +35,7 @@ type App struct {
|
||||
//binding *subsystem.Binding
|
||||
call *subsystem.Call
|
||||
menu *subsystem.Menu
|
||||
url *subsystem.URL
|
||||
dispatcher *messagedispatcher.Dispatcher
|
||||
|
||||
menuManager *menumanager.Manager
|
||||
@ -160,6 +161,19 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.options.Mac.URLHandlers != nil {
|
||||
// Start the url handler subsystem
|
||||
url, err := subsystem.NewURL(a.servicebus, a.logger, a.options.Mac.URLHandlers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.url = url
|
||||
err = a.url.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Start the eventing subsystem
|
||||
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
|
@ -46,6 +46,15 @@ int hashmap_log(void *const context, struct hashmap_element_s *const e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void filelog(const char *message) {
|
||||
FILE *fp = fopen("/tmp/wailslog.txt", "ab");
|
||||
if (fp != NULL)
|
||||
{
|
||||
fputs(message, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
// Utility function to visualise a hashmap
|
||||
void dumpHashmap(const char *name, struct hashmap_s *hashmap) {
|
||||
printf("%s = { ", name);
|
||||
@ -113,6 +122,7 @@ struct Application {
|
||||
int useToolBar;
|
||||
int hideToolbarSeparator;
|
||||
int windowBackgroundIsTranslucent;
|
||||
int hasURLHandlers;
|
||||
|
||||
// Menu
|
||||
Menu *applicationMenu;
|
||||
@ -1143,17 +1153,31 @@ void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
||||
);
|
||||
}
|
||||
|
||||
void getURL(id self, SEL selector, id event, id replyEvent) {
|
||||
id desc = msg(event, s("paramDescriptorForKeyword:"), keyDirectObject);
|
||||
id url = msg(desc, s("stringValue"));
|
||||
const char* curl = cstr(url);
|
||||
const char* message = concat("UC", curl);
|
||||
messageFromWindowCallback(message);
|
||||
MEMFREE(message);
|
||||
}
|
||||
|
||||
|
||||
void createDelegate(struct Application *app) {
|
||||
// Define delegate
|
||||
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "AppDelegate", 0);
|
||||
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSApplicationDelegate"));
|
||||
class_addMethod(delegateClass, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) no, "c@:@");
|
||||
// class_addMethod(delegateClass, s("applicationWillTerminate:"), (IMP) closeWindow, "v@:@");
|
||||
class_addMethod(delegateClass, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@");
|
||||
|
||||
// All Menu Items use a common callback
|
||||
class_addMethod(delegateClass, s("menuItemCallback:"), (IMP)menuItemCallback, "v@:@");
|
||||
|
||||
// If there are URL Handlers, register the callback method
|
||||
if( app->hasURLHandlers ) {
|
||||
class_addMethod(delegateClass, s("getUrl:withReplyEvent:"), (IMP) getURL, "i@:@@");
|
||||
}
|
||||
|
||||
// Script handler
|
||||
class_addMethod(delegateClass, s("userContentController:didReceiveScriptMessage:"), (IMP) messageHandler, "v@:@@");
|
||||
objc_registerClassPair(delegateClass);
|
||||
@ -1162,6 +1186,12 @@ void createDelegate(struct Application *app) {
|
||||
id delegate = msg((id)delegateClass, s("new"));
|
||||
objc_setAssociatedObject(delegate, "application", (id)app, OBJC_ASSOCIATION_ASSIGN);
|
||||
|
||||
// If there are URL Handlers, register a listener for them
|
||||
if( app->hasURLHandlers ) {
|
||||
id eventManager = msg(c("NSAppleEventManager"), s("sharedAppleEventManager"));
|
||||
msg(eventManager, s("setEventHandler:andSelector:forEventClass:andEventID:"), delegate, s("getUrl:withReplyEvent:"), kInternetEventClass, kAEGetURL);
|
||||
}
|
||||
|
||||
// Theme change listener
|
||||
class_addMethod(delegateClass, s("themeChanged:"), (IMP) themeChanged, "v@:@@");
|
||||
|
||||
@ -1831,6 +1861,10 @@ void SetActivationPolicy(struct Application* app, int policy) {
|
||||
app->activationPolicy = policy;
|
||||
}
|
||||
|
||||
void HasURLHandlers(struct Application* app) {
|
||||
app->hasURLHandlers = 1;
|
||||
}
|
||||
|
||||
// Quit will stop the cocoa application and free up all the memory
|
||||
// used by the application
|
||||
void Quit(struct Application *app) {
|
||||
@ -1904,6 +1938,8 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
||||
|
||||
result->activationPolicy = NSApplicationActivationPolicyRegular;
|
||||
|
||||
result->hasURLHandlers = 0;
|
||||
|
||||
return (void*) result;
|
||||
}
|
||||
|
||||
|
@ -90,5 +90,10 @@ func (a *Application) processPlatformSettings() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Process URL Handlers
|
||||
if a.config.Mac.URLHandlers != nil {
|
||||
C.HasURLHandlers(a.app)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -14,6 +14,10 @@
|
||||
// Macros to make it slightly more sane
|
||||
#define msg objc_msgSend
|
||||
|
||||
#define kInternetEventClass 'GURL'
|
||||
#define kAEGetURL 'GURL'
|
||||
#define keyDirectObject '----'
|
||||
|
||||
#define c(str) (id)objc_getClass(str)
|
||||
#define s(str) sel_registerName(str)
|
||||
#define u(str) sel_getUid(str)
|
||||
@ -118,4 +122,6 @@ void SetActivationPolicy(struct Application* app, int policy);
|
||||
|
||||
void* lookupStringConstant(id constantName);
|
||||
|
||||
void HasURLHandlers(struct Application* app);
|
||||
|
||||
#endif
|
@ -21,13 +21,14 @@ var messageParsers = map[byte]func(string) (*parsedMessage, error){
|
||||
'M': menuMessageParser,
|
||||
'T': trayMessageParser,
|
||||
'X': contextMenusMessageParser,
|
||||
'U': urlMessageParser,
|
||||
}
|
||||
|
||||
// Parse will attempt to parse the given message
|
||||
func Parse(message string) (*parsedMessage, error) {
|
||||
|
||||
if len(message) == 0 {
|
||||
return nil, fmt.Errorf("MessageParser received blank message");
|
||||
return nil, fmt.Errorf("MessageParser received blank message")
|
||||
}
|
||||
|
||||
parseMethod := messageParsers[message[0]]
|
||||
|
20
v2/internal/messagedispatcher/message/url.go
Normal file
20
v2/internal/messagedispatcher/message/url.go
Normal file
@ -0,0 +1,20 @@
|
||||
package message
|
||||
|
||||
import "fmt"
|
||||
|
||||
// urlMessageParser does what it says on the tin!
|
||||
func urlMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: URL messages must be at least 2 bytes
|
||||
if len(message) < 2 {
|
||||
return nil, fmt.Errorf("log message was an invalid length")
|
||||
}
|
||||
|
||||
// Switch on the log type
|
||||
switch message[1] {
|
||||
case 'C':
|
||||
return &parsedMessage{Topic: "url:handler", Data: message[2:]}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("url message type '%c' invalid", message[1])
|
||||
}
|
||||
}
|
94
v2/internal/subsystem/url.go
Normal file
94
v2/internal/subsystem/url.go
Normal file
@ -0,0 +1,94 @@
|
||||
package subsystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
)
|
||||
|
||||
// URL is the URL Handler subsystem. It handles messages with topics starting
|
||||
// with "url:"
|
||||
type URL struct {
|
||||
urlChannel <-chan *servicebus.Message
|
||||
|
||||
// quit flag
|
||||
shouldQuit bool
|
||||
|
||||
// Logger!
|
||||
logger *logger.Logger
|
||||
|
||||
// Context for shutdown
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
// internal waitgroup
|
||||
wg sync.WaitGroup
|
||||
|
||||
// Handlers
|
||||
handlers map[string]func(string)
|
||||
}
|
||||
|
||||
// NewURL creates a new log subsystem
|
||||
func NewURL(bus *servicebus.ServiceBus, logger *logger.Logger, handlers map[string]func(string)) (*URL, error) {
|
||||
|
||||
// Subscribe to log messages
|
||||
urlChannel, err := bus.Subscribe("url")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
result := &URL{
|
||||
urlChannel: urlChannel,
|
||||
logger: logger,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
handlers: handlers,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Start the subsystem
|
||||
func (u *URL) Start() error {
|
||||
|
||||
u.wg.Add(1)
|
||||
|
||||
// Spin off a go routine
|
||||
go func() {
|
||||
defer u.logger.Trace("URL Shutdown")
|
||||
|
||||
for u.shouldQuit == false {
|
||||
select {
|
||||
case <-u.ctx.Done():
|
||||
u.wg.Done()
|
||||
return
|
||||
case urlMessage := <-u.urlChannel:
|
||||
messageType := strings.TrimPrefix(urlMessage.Topic(), "url:")
|
||||
switch messageType {
|
||||
case "handler":
|
||||
url := urlMessage.Data().(string)
|
||||
splitURL := strings.Split(url, ":")
|
||||
protocol := splitURL[0]
|
||||
callback, ok := u.handlers[protocol]
|
||||
if ok {
|
||||
go callback(url)
|
||||
}
|
||||
default:
|
||||
u.logger.Error("unknown url message: %+v", urlMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *URL) Close() {
|
||||
u.cancel()
|
||||
u.wg.Wait()
|
||||
}
|
@ -20,4 +20,5 @@ type Options struct {
|
||||
TrayMenus []*menu.TrayMenu
|
||||
ContextMenus []*menu.ContextMenu
|
||||
ActivationPolicy ActivationPolicy
|
||||
URLHandlers map[string]func(string)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user