mirror of https://github.com/adamdruppe/arsd.git
Merge branch 'master' of git://192.168.1.10/home/me/arsd
This commit is contained in:
commit
1ae1cbe7ec
6
audio.d
6
audio.d
|
@ -4,8 +4,6 @@ module arsd.audio;
|
||||||
import sdl.SDL;
|
import sdl.SDL;
|
||||||
import sdl.SDL_mixer;
|
import sdl.SDL_mixer;
|
||||||
|
|
||||||
import std.string;
|
|
||||||
|
|
||||||
import arsd.engine;
|
import arsd.engine;
|
||||||
|
|
||||||
bool audioIsLoaded; // potential hack material
|
bool audioIsLoaded; // potential hack material
|
||||||
|
@ -15,7 +13,7 @@ class Sound {
|
||||||
this(char[] filename){
|
this(char[] filename){
|
||||||
if(!audioIsLoaded)
|
if(!audioIsLoaded)
|
||||||
return;
|
return;
|
||||||
sfx = Mix_LoadWAV(std.string.toStringz(filename));
|
sfx = Mix_LoadWAV((filename ~ "\0").ptr);
|
||||||
if(sfx is null)
|
if(sfx is null)
|
||||||
throw new Exception(immutableString("Sound load " ~ filename));
|
throw new Exception(immutableString("Sound load " ~ filename));
|
||||||
}
|
}
|
||||||
|
@ -45,7 +43,7 @@ class Music {
|
||||||
this(char[] filename){
|
this(char[] filename){
|
||||||
if(!audioIsLoaded)
|
if(!audioIsLoaded)
|
||||||
return;
|
return;
|
||||||
mus = Mix_LoadMUS(std.string.toStringz(filename));
|
mus = Mix_LoadMUS((filename~"\0").ptr);
|
||||||
if(mus is null)
|
if(mus is null)
|
||||||
throw new Exception(immutableString("Music load " ~ filename));
|
throw new Exception(immutableString("Music load " ~ filename));
|
||||||
}
|
}
|
||||||
|
|
19
engine.d
19
engine.d
|
@ -21,13 +21,13 @@ pragma(lib, "GL");
|
||||||
import sdl.SDL;
|
import sdl.SDL;
|
||||||
import sdl.SDL_net;
|
import sdl.SDL_net;
|
||||||
|
|
||||||
import std.string;
|
|
||||||
version(D_Version2) {
|
version(D_Version2) {
|
||||||
import random = core.stdc.stdlib;
|
import random = core.stdc.stdlib;
|
||||||
alias random.srand srand;
|
alias random.srand srand;
|
||||||
|
|
||||||
import std.conv;
|
char[] convToString(int a) {
|
||||||
char[] convToString(T)(T t) { return to!(char[])(t); }
|
return null;
|
||||||
|
}
|
||||||
string immutableString(in char[] a) { return a.idup; }
|
string immutableString(in char[] a) { return a.idup; }
|
||||||
} else {
|
} else {
|
||||||
import random = std.random;
|
import random = std.random;
|
||||||
|
@ -49,7 +49,12 @@ version(D_Version2)
|
||||||
else
|
else
|
||||||
import std.stdarg;
|
import std.stdarg;
|
||||||
|
|
||||||
import std.stdio;
|
version(D_Version2) {
|
||||||
|
import core.stdc.stdio;
|
||||||
|
void writefln(string s) { printf("%*s", s.length, s.ptr); }
|
||||||
|
void writefln(string s, int i) { printf(s.ptr, i); }
|
||||||
|
} else
|
||||||
|
import std.stdio;
|
||||||
//version(linux) pragma(lib, "kbhit.o");
|
//version(linux) pragma(lib, "kbhit.o");
|
||||||
|
|
||||||
int randomNumber(int min, int max){
|
int randomNumber(int min, int max){
|
||||||
|
@ -317,7 +322,7 @@ class Engine{
|
||||||
|
|
||||||
IPaddress ip;
|
IPaddress ip;
|
||||||
|
|
||||||
if(SDLNet_ResolveHost(&ip, std.string.toStringz(whom), NET_PORT) == -1)
|
if(SDLNet_ResolveHost(&ip, (whom~"\0").ptr, NET_PORT) == -1)
|
||||||
throw new Exception("Resolve host");
|
throw new Exception("Resolve host");
|
||||||
|
|
||||||
clientsock = SDLNet_TCP_Open(&ip);
|
clientsock = SDLNet_TCP_Open(&ip);
|
||||||
|
@ -661,7 +666,7 @@ class Engine{
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTitle(in char[] title){
|
void setTitle(in char[] title){
|
||||||
SDL_WM_SetCaption(std.string.toStringz(title), null);
|
SDL_WM_SetCaption((title~"\0").ptr, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool buttonWasPressed(Buttons button, int which = 0){
|
bool buttonWasPressed(Buttons button, int which = 0){
|
||||||
|
@ -979,7 +984,7 @@ class Engine{
|
||||||
buttonLagQueueEnd[button][which] = 0;
|
buttonLagQueueEnd[button][which] = 0;
|
||||||
} else {
|
} else {
|
||||||
if(when < globalTimer)
|
if(when < globalTimer)
|
||||||
throw new Exception(immutableString("Impossible control timing " ~ convToString(when) ~ " @ " ~ convToString(globalTimer)));
|
throw new Exception(immutableString("Impossible control timing"));// " ~ convToString(when) ~ " @ " ~ convToString(globalTimer)));
|
||||||
buttonsDown[button][which] = type;
|
buttonsDown[button][which] = type;
|
||||||
buttonsChecked[button][which] = false;
|
buttonsChecked[button][which] = false;
|
||||||
}
|
}
|
||||||
|
|
222
http2.d
222
http2.d
|
@ -102,7 +102,11 @@ unittest {
|
||||||
// FIXME: multipart encoded file uploads needs implementation
|
// FIXME: multipart encoded file uploads needs implementation
|
||||||
// future: do web client api stuff
|
// future: do web client api stuff
|
||||||
|
|
||||||
__gshared bool defaultVerifyPeer = true; // public but intentionally undocumented
|
private __gshared bool defaultVerifyPeer_ = true;
|
||||||
|
|
||||||
|
void defaultVerifyPeer(bool v) {
|
||||||
|
defaultVerifyPeer_ = v;
|
||||||
|
}
|
||||||
|
|
||||||
debug import std.stdio;
|
debug import std.stdio;
|
||||||
|
|
||||||
|
@ -953,13 +957,30 @@ class HttpRequest {
|
||||||
/++
|
/++
|
||||||
Proxy to use for this request. It should be a URL or `null`.
|
Proxy to use for this request. It should be a URL or `null`.
|
||||||
|
|
||||||
This must be sent before you call `send`.
|
This must be sent before you call [send].
|
||||||
|
|
||||||
History:
|
History:
|
||||||
Added April 12, 2021 (dub v9.5)
|
Added April 12, 2021 (dub v9.5)
|
||||||
+/
|
+/
|
||||||
string proxy;
|
string proxy;
|
||||||
|
|
||||||
|
/++
|
||||||
|
For https connections, if this is `true`, it will fail to connect if the TLS certificate can not be
|
||||||
|
verified. Setting this to `false` will skip this check and allow the connection to continue anyway.
|
||||||
|
|
||||||
|
When the [HttpRequest] is constructed from a [HttpClient], it will inherit the value from the client
|
||||||
|
instead of using the `= true` here. You can change this value any time before you call [send] (which
|
||||||
|
is done implicitly if you call [waitForCompletion]).
|
||||||
|
|
||||||
|
History:
|
||||||
|
Added April 5, 2022 (dub v10.8)
|
||||||
|
|
||||||
|
Prior to this, it always used the global (but undocumented) `defaultVerifyPeer` setting, and sometimes
|
||||||
|
even if it was true, it would skip the verification. Now, it always respects this local setting.
|
||||||
|
+/
|
||||||
|
bool verifyPeer = true;
|
||||||
|
|
||||||
|
|
||||||
/// Final url after any redirections
|
/// Final url after any redirections
|
||||||
string finalUrl;
|
string finalUrl;
|
||||||
|
|
||||||
|
@ -1215,13 +1236,13 @@ class HttpRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket getOpenSocketOnHost(string proxy, string host, ushort port, bool ssl, string unixSocketPath) {
|
Socket getOpenSocketOnHost(string proxy, string host, ushort port, bool ssl, string unixSocketPath, bool verifyPeer) {
|
||||||
Socket openNewConnection() {
|
Socket openNewConnection() {
|
||||||
Socket socket;
|
Socket socket;
|
||||||
if(ssl) {
|
if(ssl) {
|
||||||
version(with_openssl) {
|
version(with_openssl) {
|
||||||
loadOpenSsl();
|
loadOpenSsl();
|
||||||
socket = new SslClientSocket(family(unixSocketPath), SocketType.STREAM, host, defaultVerifyPeer);
|
socket = new SslClientSocket(family(unixSocketPath), SocketType.STREAM, host, verifyPeer);
|
||||||
} else
|
} else
|
||||||
throw new Exception("SSL not compiled in");
|
throw new Exception("SSL not compiled in");
|
||||||
} else
|
} else
|
||||||
|
@ -1449,7 +1470,7 @@ class HttpRequest {
|
||||||
Socket socket;
|
Socket socket;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
socket = getOpenSocketOnHost(pc.proxy, pc.requestParameters.host, pc.requestParameters.port, pc.requestParameters.ssl, pc.requestParameters.unixSocketPath);
|
socket = getOpenSocketOnHost(pc.proxy, pc.requestParameters.host, pc.requestParameters.port, pc.requestParameters.ssl, pc.requestParameters.unixSocketPath, pc.verifyPeer);
|
||||||
} catch(ProxyException e) {
|
} catch(ProxyException e) {
|
||||||
// connection refused or timed out (I should disambiguate somehow)...
|
// connection refused or timed out (I should disambiguate somehow)...
|
||||||
pc.state = HttpRequest.State.aborted;
|
pc.state = HttpRequest.State.aborted;
|
||||||
|
@ -1474,7 +1495,7 @@ class HttpRequest {
|
||||||
removeFromPending[removeFromPendingCount++] = pc;
|
removeFromPending[removeFromPendingCount++] = pc;
|
||||||
continue;
|
continue;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
// connection failed due to other user error
|
// connection failed due to other user error or SSL (i should disambiguate somehow)...
|
||||||
pc.state = HttpRequest.State.aborted;
|
pc.state = HttpRequest.State.aborted;
|
||||||
|
|
||||||
pc.responseData.code = 2;
|
pc.responseData.code = 2;
|
||||||
|
@ -2226,7 +2247,7 @@ struct HttpRequestsAsTheyComplete {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
//
|
||||||
struct HttpRequestParameters {
|
struct HttpRequestParameters {
|
||||||
// FIXME: implement these
|
// FIXME: implement these
|
||||||
//Duration timeoutTotal; // the whole request must finish in this time or else it fails,even if data is still trickling in
|
//Duration timeoutTotal; // the whole request must finish in this time or else it fails,even if data is still trickling in
|
||||||
|
@ -2255,7 +2276,7 @@ struct HttpRequestParameters {
|
||||||
string contentType; ///
|
string contentType; ///
|
||||||
ubyte[] bodyData; ///
|
ubyte[] bodyData; ///
|
||||||
|
|
||||||
string unixSocketPath;
|
string unixSocketPath; ///
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IHttpClient {
|
interface IHttpClient {
|
||||||
|
@ -2333,6 +2354,15 @@ class HttpClient {
|
||||||
this.certFormat = certFormat;
|
this.certFormat = certFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Sets whether [HttpRequest]s created through this object (with [navigateTo], [request], etc.), will have the
|
||||||
|
value of [HttpRequest.verifyPeer] of true or false upon construction.
|
||||||
|
|
||||||
|
History:
|
||||||
|
Added April 5, 2022 (dub v10.8). Previously, there was an undocumented global value used.
|
||||||
|
+/
|
||||||
|
bool defaultVerifyPeer = true;
|
||||||
|
|
||||||
// FIXME: try to not make these static
|
// FIXME: try to not make these static
|
||||||
private static string certFilename;
|
private static string certFilename;
|
||||||
private static string keyFilename;
|
private static string keyFilename;
|
||||||
|
@ -2387,6 +2417,8 @@ class HttpClient {
|
||||||
|
|
||||||
auto request = new HttpRequest(this, uri, method, cache, defaultTimeout, proxyToUse);
|
auto request = new HttpRequest(this, uri, method, cache, defaultTimeout, proxyToUse);
|
||||||
|
|
||||||
|
request.verifyPeer = this.defaultVerifyPeer;
|
||||||
|
|
||||||
request.requestParameters.userAgent = userAgent;
|
request.requestParameters.userAgent = userAgent;
|
||||||
request.requestParameters.authorization = authorization;
|
request.requestParameters.authorization = authorization;
|
||||||
|
|
||||||
|
@ -2418,7 +2450,11 @@ class HttpClient {
|
||||||
private string currentDomain;
|
private string currentDomain;
|
||||||
private ICache cache;
|
private ICache cache;
|
||||||
|
|
||||||
|
/++
|
||||||
|
|
||||||
|
+/
|
||||||
this(ICache cache = null) {
|
this(ICache cache = null) {
|
||||||
|
this.defaultVerifyPeer = .defaultVerifyPeer_;
|
||||||
this.cache = cache;
|
this.cache = cache;
|
||||||
loadDefaultProxy();
|
loadDefaultProxy();
|
||||||
}
|
}
|
||||||
|
@ -2748,6 +2784,7 @@ version(use_openssl) {
|
||||||
struct SSL_CTX {}
|
struct SSL_CTX {}
|
||||||
struct SSL_METHOD {}
|
struct SSL_METHOD {}
|
||||||
enum SSL_VERIFY_NONE = 0;
|
enum SSL_VERIFY_NONE = 0;
|
||||||
|
enum SSL_VERIFY_PEER = 1;
|
||||||
|
|
||||||
struct ossllib {
|
struct ossllib {
|
||||||
__gshared static extern(C) {
|
__gshared static extern(C) {
|
||||||
|
@ -2780,6 +2817,16 @@ version(use_openssl) {
|
||||||
|
|
||||||
void function(SSL_CTX*, void function(SSL*, char* line)) SSL_CTX_set_keylog_callback;
|
void function(SSL_CTX*, void function(SSL*, char* line)) SSL_CTX_set_keylog_callback;
|
||||||
|
|
||||||
|
int function(SSL_CTX*) SSL_CTX_set_default_verify_paths;
|
||||||
|
|
||||||
|
X509_STORE* function(SSL_CTX*) SSL_CTX_get_cert_store;
|
||||||
|
c_long function(const SSL* ssl) SSL_get_verify_result;
|
||||||
|
|
||||||
|
/+
|
||||||
|
SSL_CTX_load_verify_locations
|
||||||
|
SSL_CTX_set_client_CA_list
|
||||||
|
+/
|
||||||
|
|
||||||
|
|
||||||
// client cert things
|
// client cert things
|
||||||
void function (SSL_CTX *ctx, int function(SSL *ssl, X509 **x509, EVP_PKEY **pkey)) SSL_CTX_set_client_cert_cb;
|
void function (SSL_CTX *ctx, int function(SSL *ssl, X509 **x509, EVP_PKEY **pkey)) SSL_CTX_set_client_cert_cb;
|
||||||
|
@ -2791,6 +2838,7 @@ version(use_openssl) {
|
||||||
alias pem_password_cb = int function(char* buffer, int bufferSize, int rwflag, void* userPointer);
|
alias pem_password_cb = int function(char* buffer, int bufferSize, int rwflag, void* userPointer);
|
||||||
|
|
||||||
struct X509;
|
struct X509;
|
||||||
|
struct X509_STORE;
|
||||||
struct EVP_PKEY;
|
struct EVP_PKEY;
|
||||||
|
|
||||||
import core.stdc.config;
|
import core.stdc.config;
|
||||||
|
@ -2804,7 +2852,10 @@ version(use_openssl) {
|
||||||
|
|
||||||
void function(ulong, void*) OPENSSL_init_crypto;
|
void function(ulong, void*) OPENSSL_init_crypto;
|
||||||
|
|
||||||
void function(FILE*) ERR_print_errors_fp;
|
void function(print_errors_cb, void*) ERR_print_errors_cb;
|
||||||
|
|
||||||
|
void function(X509*) X509_free;
|
||||||
|
int function(X509_STORE*, X509*) X509_STORE_add_cert;
|
||||||
|
|
||||||
|
|
||||||
X509* function(FILE *fp, X509 **x, pem_password_cb *cb, void *u) PEM_read_X509;
|
X509* function(FILE *fp, X509 **x, pem_password_cb *cb, void *u) PEM_read_X509;
|
||||||
|
@ -2812,9 +2863,31 @@ version(use_openssl) {
|
||||||
|
|
||||||
EVP_PKEY* function(FILE *fp, EVP_PKEY **a) d2i_PrivateKey_fp;
|
EVP_PKEY* function(FILE *fp, EVP_PKEY **a) d2i_PrivateKey_fp;
|
||||||
X509* function(FILE *fp, X509 **x) d2i_X509_fp;
|
X509* function(FILE *fp, X509 **x) d2i_X509_fp;
|
||||||
|
|
||||||
|
X509* function(X509** a, const(ubyte*)* pp, c_long length) d2i_X509;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C)
|
||||||
|
alias print_errors_cb = int function(const char*, size_t, void*);
|
||||||
|
|
||||||
|
int SSL_CTX_set_default_verify_paths(SSL_CTX* a) {
|
||||||
|
if(ossllib.SSL_CTX_set_default_verify_paths)
|
||||||
|
return ossllib.SSL_CTX_set_default_verify_paths(a);
|
||||||
|
else throw new Exception("SSL_CTX_set_default_verify_paths not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
c_long SSL_get_verify_result(const SSL* ssl) {
|
||||||
|
if(ossllib.SSL_get_verify_result)
|
||||||
|
return ossllib.SSL_get_verify_result(ssl);
|
||||||
|
else throw new Exception("SSL_get_verify_result not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
X509_STORE* SSL_CTX_get_cert_store(SSL_CTX* a) {
|
||||||
|
if(ossllib.SSL_CTX_get_cert_store)
|
||||||
|
return ossllib.SSL_CTX_get_cert_store(a);
|
||||||
|
else throw new Exception("SSL_CTX_get_cert_store not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
SSL_CTX* SSL_CTX_new(const SSL_METHOD* a) {
|
SSL_CTX* SSL_CTX_new(const SSL_METHOD* a) {
|
||||||
if(ossllib.SSL_CTX_new)
|
if(ossllib.SSL_CTX_new)
|
||||||
|
@ -2841,6 +2914,12 @@ version(use_openssl) {
|
||||||
else throw new Exception("SSL_CTX_set_client_cert_cb not loaded");
|
else throw new Exception("SSL_CTX_set_client_cert_cb not loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
X509* d2i_X509(X509** a, const(ubyte*)* pp, c_long length) {
|
||||||
|
if(eallib.d2i_X509)
|
||||||
|
return eallib.d2i_X509(a, pp, length);
|
||||||
|
else throw new Exception("d2i_X509 not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
X509* PEM_read_X509(FILE *fp, X509 **x, pem_password_cb *cb, void *u) {
|
X509* PEM_read_X509(FILE *fp, X509 **x, pem_password_cb *cb, void *u) {
|
||||||
if(eallib.PEM_read_X509)
|
if(eallib.PEM_read_X509)
|
||||||
return eallib.PEM_read_X509(fp, x, cb, u);
|
return eallib.PEM_read_X509(fp, x, cb, u);
|
||||||
|
@ -2920,10 +2999,29 @@ version(use_openssl) {
|
||||||
return ossllib.TLS_client_method();
|
return ossllib.TLS_client_method();
|
||||||
else throw new Exception("TLS_client_method not loaded");
|
else throw new Exception("TLS_client_method not loaded");
|
||||||
}
|
}
|
||||||
void ERR_print_errors_fp(FILE* a) {
|
void ERR_print_errors_cb(print_errors_cb cb, void* u) {
|
||||||
if(eallib.ERR_print_errors_fp)
|
if(eallib.ERR_print_errors_cb)
|
||||||
return eallib.ERR_print_errors_fp(a);
|
return eallib.ERR_print_errors_cb(cb, u);
|
||||||
else throw new Exception("ERR_print_errors_fp not loaded");
|
else throw new Exception("ERR_print_errors_cb not loaded");
|
||||||
|
}
|
||||||
|
void X509_free(X509* x) {
|
||||||
|
if(eallib.X509_free)
|
||||||
|
return eallib.X509_free(x);
|
||||||
|
else throw new Exception("X509_free not loaded");
|
||||||
|
}
|
||||||
|
int X509_STORE_add_cert(X509_STORE* s, X509* x) {
|
||||||
|
if(eallib.X509_STORE_add_cert)
|
||||||
|
return eallib.X509_STORE_add_cert(s, x);
|
||||||
|
else throw new Exception("X509_STORE_add_cert not loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern(C)
|
||||||
|
int collectSslErrors(const char* ptr, size_t len, void* user) @trusted {
|
||||||
|
string* s = cast(string*) user;
|
||||||
|
|
||||||
|
(*s) ~= ptr[0 .. len];
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern(C)
|
extern(C)
|
||||||
|
@ -2971,8 +3069,14 @@ version(use_openssl) {
|
||||||
if(ossllib_handle is null)
|
if(ossllib_handle is null)
|
||||||
ossllib_handle = dlopen("libssl.so", RTLD_NOW);
|
ossllib_handle = dlopen("libssl.so", RTLD_NOW);
|
||||||
} else version(Windows) {
|
} else version(Windows) {
|
||||||
ossllib_handle = LoadLibraryW("libssl32.dll"w.ptr);
|
version(X86_64)
|
||||||
oeaylib_handle = LoadLibraryW("libeay32.dll"w.ptr);
|
ossllib_handle = LoadLibraryW("libssl-1_1-x64.dll"w.ptr);
|
||||||
|
if(ossllib_handle is null)
|
||||||
|
ossllib_handle = LoadLibraryW("libssl32.dll"w.ptr);
|
||||||
|
version(X86_64)
|
||||||
|
oeaylib_handle = LoadLibraryW("libcrypto-1_1-x64.dll"w.ptr);
|
||||||
|
if(oeaylib_handle is null)
|
||||||
|
oeaylib_handle = LoadLibraryW("libeay32.dll"w.ptr);
|
||||||
|
|
||||||
if(ossllib_handle is null) {
|
if(ossllib_handle is null) {
|
||||||
ossllib_handle = LoadLibraryW("ssleay32.dll"w.ptr);
|
ossllib_handle = LoadLibraryW("ssleay32.dll"w.ptr);
|
||||||
|
@ -3068,14 +3172,22 @@ version(use_openssl) {
|
||||||
private void initSsl(bool verifyPeer, string hostname) {
|
private void initSsl(bool verifyPeer, string hostname) {
|
||||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
ctx = SSL_CTX_new(SSLv23_client_method());
|
||||||
assert(ctx !is null);
|
assert(ctx !is null);
|
||||||
|
|
||||||
|
SSL_CTX_set_default_verify_paths(ctx);
|
||||||
|
version(Windows)
|
||||||
|
loadCertificatesFromRegistry(ctx);
|
||||||
|
|
||||||
debug SSL_CTX_keylog_cb_func(ctx, &write_to_file);
|
debug SSL_CTX_keylog_cb_func(ctx, &write_to_file);
|
||||||
ssl = SSL_new(ctx);
|
ssl = SSL_new(ctx);
|
||||||
|
|
||||||
if(hostname.length)
|
if(hostname.length)
|
||||||
SSL_set_tlsext_host_name(ssl, toStringz(hostname));
|
SSL_set_tlsext_host_name(ssl, toStringz(hostname));
|
||||||
|
|
||||||
if(!verifyPeer)
|
if(verifyPeer)
|
||||||
|
SSL_set_verify(ssl, SSL_VERIFY_PEER, null);
|
||||||
|
else
|
||||||
SSL_set_verify(ssl, SSL_VERIFY_NONE, null);
|
SSL_set_verify(ssl, SSL_VERIFY_NONE, null);
|
||||||
|
|
||||||
SSL_set_fd(ssl, cast(int) this.handle); // on win64 it is necessary to truncate, but the value is never large anyway see http://openssl.6102.n7.nabble.com/Sockets-windows-64-bit-td36169.html
|
SSL_set_fd(ssl, cast(int) this.handle); // on win64 it is necessary to truncate, but the value is never large anyway see http://openssl.6102.n7.nabble.com/Sockets-windows-64-bit-td36169.html
|
||||||
|
|
||||||
|
|
||||||
|
@ -3132,11 +3244,13 @@ version(use_openssl) {
|
||||||
@trusted
|
@trusted
|
||||||
void do_ssl_connect() {
|
void do_ssl_connect() {
|
||||||
if(SSL_connect(ssl) == -1) {
|
if(SSL_connect(ssl) == -1) {
|
||||||
ERR_print_errors_fp(core.stdc.stdio.stderr);
|
string str;
|
||||||
|
ERR_print_errors_cb(&collectSslErrors, &str);
|
||||||
int i;
|
int i;
|
||||||
|
auto err = SSL_get_verify_result(ssl);
|
||||||
//printf("wtf\n");
|
//printf("wtf\n");
|
||||||
//scanf("%d\n", i);
|
//scanf("%d\n", i);
|
||||||
throw new Exception("ssl connect");
|
throw new Exception("ssl connect failed " ~ str ~ " // " ~ to!string(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3146,11 +3260,12 @@ version(use_openssl) {
|
||||||
debug(arsd_http2_verbose) writeln("ssl writing ", buf.length);
|
debug(arsd_http2_verbose) writeln("ssl writing ", buf.length);
|
||||||
auto retval = SSL_write(ssl, buf.ptr, cast(uint) buf.length);
|
auto retval = SSL_write(ssl, buf.ptr, cast(uint) buf.length);
|
||||||
if(retval == -1) {
|
if(retval == -1) {
|
||||||
ERR_print_errors_fp(core.stdc.stdio.stderr);
|
string str;
|
||||||
|
ERR_print_errors_cb(&collectSslErrors, &str);
|
||||||
int i;
|
int i;
|
||||||
//printf("wtf\n");
|
//printf("wtf\n");
|
||||||
//scanf("%d\n", i);
|
//scanf("%d\n", i);
|
||||||
throw new Exception("ssl send");
|
throw new Exception("ssl send failed " ~ str);
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -3165,11 +3280,12 @@ version(use_openssl) {
|
||||||
auto retval = SSL_read(ssl, buf.ptr, cast(int)buf.length);
|
auto retval = SSL_read(ssl, buf.ptr, cast(int)buf.length);
|
||||||
debug(arsd_http2_verbose) writeln("ssl_read after");
|
debug(arsd_http2_verbose) writeln("ssl_read after");
|
||||||
if(retval == -1) {
|
if(retval == -1) {
|
||||||
ERR_print_errors_fp(core.stdc.stdio.stderr);
|
string str;
|
||||||
|
ERR_print_errors_cb(&collectSslErrors, &str);
|
||||||
int i;
|
int i;
|
||||||
//printf("wtf\n");
|
//printf("wtf\n");
|
||||||
//scanf("%d\n", i);
|
//scanf("%d\n", i);
|
||||||
throw new Exception("ssl receive");
|
throw new Exception("ssl receive failed " ~ str);
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -3664,7 +3780,7 @@ class WebSocket {
|
||||||
if(ssl) {
|
if(ssl) {
|
||||||
version(with_openssl) {
|
version(with_openssl) {
|
||||||
loadOpenSsl();
|
loadOpenSsl();
|
||||||
socket = new SslClientSocket(family(uri.unixSocketPath), SocketType.STREAM, host, defaultVerifyPeer);
|
socket = new SslClientSocket(family(uri.unixSocketPath), SocketType.STREAM, host, config.verifyPeer);
|
||||||
} else
|
} else
|
||||||
throw new Exception("SSL not compiled in");
|
throw new Exception("SSL not compiled in");
|
||||||
} else
|
} else
|
||||||
|
@ -4018,6 +4134,18 @@ class WebSocket {
|
||||||
Added March 31, 2021 (included in dub version 9.4)
|
Added March 31, 2021 (included in dub version 9.4)
|
||||||
+/
|
+/
|
||||||
Duration timeoutFromInactivity = 1.minutes;
|
Duration timeoutFromInactivity = 1.minutes;
|
||||||
|
|
||||||
|
/++
|
||||||
|
For https connections, if this is `true`, it will fail to connect if the TLS certificate can not be
|
||||||
|
verified. Setting this to `false` will skip this check and allow the connection to continue anyway.
|
||||||
|
|
||||||
|
History:
|
||||||
|
Added April 5, 2022 (dub v10.8)
|
||||||
|
|
||||||
|
Prior to this, it always used the global (but undocumented) `defaultVerifyPeer` setting, and sometimes
|
||||||
|
even if it was true, it would skip the verification. Now, it always respects this local setting.
|
||||||
|
+/
|
||||||
|
bool verifyPeer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
|
@ -4676,6 +4804,52 @@ public {
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Windows) {
|
version(Windows) {
|
||||||
|
pragma(lib, "crypt32");
|
||||||
|
import core.sys.windows.wincrypt;
|
||||||
|
extern(Windows)
|
||||||
|
PCCERT_CONTEXT CertEnumCertificatesInStore(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
|
||||||
|
|
||||||
|
void loadCertificatesFromRegistry(SSL_CTX* ctx) {
|
||||||
|
auto store = CertOpenSystemStore(0, "ROOT");
|
||||||
|
if(store is null) {
|
||||||
|
// import std.stdio; writeln("failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scope(exit)
|
||||||
|
CertCloseStore(store, 0);
|
||||||
|
|
||||||
|
X509_STORE* ssl_store = SSL_CTX_get_cert_store(ctx);
|
||||||
|
PCCERT_CONTEXT c;
|
||||||
|
while((c = CertEnumCertificatesInStore(store, c)) !is null) {
|
||||||
|
FILETIME na = c.pCertInfo.NotAfter;
|
||||||
|
SYSTEMTIME st;
|
||||||
|
FileTimeToSystemTime(&na, &st);
|
||||||
|
|
||||||
|
/+
|
||||||
|
_CRYPTOAPI_BLOB i = cast() c.pCertInfo.Issuer;
|
||||||
|
|
||||||
|
char[256] buffer;
|
||||||
|
auto p = CertNameToStrA(X509_ASN_ENCODING, &i, CERT_SIMPLE_NAME_STR, buffer.ptr, cast(int) buffer.length);
|
||||||
|
import std.stdio; writeln(buffer[0 .. p]);
|
||||||
|
+/
|
||||||
|
|
||||||
|
if(st.wYear <= 2021) {
|
||||||
|
// see: https://www.openssl.org/blog/blog/2021/09/13/LetsEncryptRootCertExpire/
|
||||||
|
continue; // no point keeping an expired root cert and it can break Let's Encrypt anyway
|
||||||
|
}
|
||||||
|
|
||||||
|
const(ubyte)* thing = c.pbCertEncoded;
|
||||||
|
auto x509 = d2i_X509(null, &thing, c.cbCertEncoded);
|
||||||
|
if (x509) {
|
||||||
|
auto success = X509_STORE_add_cert(ssl_store, x509);
|
||||||
|
X509_free(x509);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CertFreeCertificateContext(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// because i use the FILE* in PEM_read_X509 and friends
|
// because i use the FILE* in PEM_read_X509 and friends
|
||||||
// gotta use this to bridge the MS C runtime functions
|
// gotta use this to bridge the MS C runtime functions
|
||||||
// might be able to just change those to only use the BIO versions
|
// might be able to just change those to only use the BIO versions
|
||||||
|
|
97
minigui.d
97
minigui.d
|
@ -327,6 +327,9 @@ version(Windows) {
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Windows) {
|
version(Windows) {
|
||||||
|
version(minigui_manifest) {} else version=minigui_no_manifest;
|
||||||
|
|
||||||
|
version(minigui_no_manifest) {} else
|
||||||
static if(__VERSION__ >= 2_083)
|
static if(__VERSION__ >= 2_083)
|
||||||
version(CRuntime_Microsoft) { // FIXME: mingw?
|
version(CRuntime_Microsoft) { // FIXME: mingw?
|
||||||
// assume we want commctrl6 whenever possible since there's really no reason not to
|
// assume we want commctrl6 whenever possible since there's really no reason not to
|
||||||
|
@ -465,6 +468,15 @@ version(Windows) {
|
||||||
+/
|
+/
|
||||||
class Widget : ReflectableProperties {
|
class Widget : ReflectableProperties {
|
||||||
|
|
||||||
|
private bool willDraw() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool skipPaintCycle() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/+
|
/+
|
||||||
/++
|
/++
|
||||||
Calling this directly after constructor can give you a reflectable object as-needed so you don't pay for what you don't need.
|
Calling this directly after constructor can give you a reflectable object as-needed so you don't pay for what you don't need.
|
||||||
|
@ -1780,6 +1792,9 @@ class Widget : ReflectableProperties {
|
||||||
if(hidden)
|
if(hidden)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(skipPaintCycle())
|
||||||
|
return;
|
||||||
|
|
||||||
int paintX = x;
|
int paintX = x;
|
||||||
int paintY = y;
|
int paintY = y;
|
||||||
if(this.useNativeDrawing()) {
|
if(this.useNativeDrawing()) {
|
||||||
|
@ -4204,6 +4219,19 @@ template classStaticallyEmits(This, EventType) {
|
||||||
class NestedChildWindowWidget : Widget {
|
class NestedChildWindowWidget : Widget {
|
||||||
SimpleWindow win;
|
SimpleWindow win;
|
||||||
|
|
||||||
|
/++
|
||||||
|
Used on X to send focus to the appropriate child window when requested by the window manager.
|
||||||
|
|
||||||
|
Normally returns its own nested window. Can also return another child or null to revert to the parent
|
||||||
|
if you override it in a child class.
|
||||||
|
|
||||||
|
History:
|
||||||
|
Added April 2, 2022 (dub v10.8)
|
||||||
|
+/
|
||||||
|
SimpleWindow focusableWindow() {
|
||||||
|
return win;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
// win = new SimpleWindow(640, 480, null, OpenGlOptions.yes, Resizability.automaticallyScaleIfPossible, WindowTypes.nestedChild, WindowFlags.normal, getParentWindow(parent));
|
// win = new SimpleWindow(640, 480, null, OpenGlOptions.yes, Resizability.automaticallyScaleIfPossible, WindowTypes.nestedChild, WindowFlags.normal, getParentWindow(parent));
|
||||||
this(SimpleWindow win, Widget parent) {
|
this(SimpleWindow win, Widget parent) {
|
||||||
|
@ -4236,6 +4264,18 @@ class NestedChildWindowWidget : Widget {
|
||||||
return pwin;
|
return pwin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Called upon the nested window being destroyed.
|
||||||
|
Remember the window has already been destroyed at
|
||||||
|
this point, so don't use the native handle for anything.
|
||||||
|
|
||||||
|
History:
|
||||||
|
Added April 3, 2022 (dub v10.8)
|
||||||
|
+/
|
||||||
|
protected void dispose() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected void windowsetup(SimpleWindow w) {
|
protected void windowsetup(SimpleWindow w) {
|
||||||
/*
|
/*
|
||||||
win.onFocusChange = (bool getting) {
|
win.onFocusChange = (bool getting) {
|
||||||
|
@ -4244,6 +4284,23 @@ class NestedChildWindowWidget : Widget {
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/+
|
||||||
|
win.onFocusChange = (bool getting) {
|
||||||
|
if(getting) {
|
||||||
|
this.parentWindow.focusedWidget = this;
|
||||||
|
this.emit!FocusEvent();
|
||||||
|
this.emit!FocusInEvent();
|
||||||
|
} else {
|
||||||
|
this.emit!BlurEvent();
|
||||||
|
this.emit!FocusOutEvent();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
+/
|
||||||
|
|
||||||
|
win.onDestroyed = () {
|
||||||
|
this.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
version(win32_widgets) {
|
version(win32_widgets) {
|
||||||
Widget.nativeMapping[win.hwnd] = this;
|
Widget.nativeMapping[win.hwnd] = this;
|
||||||
this.originalWindowProcedure = cast(WNDPROC) SetWindowLongPtr(win.hwnd, GWL_WNDPROC, cast(size_t) &HookedWndProc);
|
this.originalWindowProcedure = cast(WNDPROC) SetWindowLongPtr(win.hwnd, GWL_WNDPROC, cast(size_t) &HookedWndProc);
|
||||||
|
@ -4259,8 +4316,7 @@ class NestedChildWindowWidget : Widget {
|
||||||
parentWindow.dispatchMouseEvent(e);
|
parentWindow.dispatchMouseEvent(e);
|
||||||
},
|
},
|
||||||
(KeyEvent e) {
|
(KeyEvent e) {
|
||||||
//import std.stdio;
|
//import std.stdio; writefln("%s %x %s", cast(void*) win, cast(uint) e.key, e.key);
|
||||||
//writefln("%x %s", cast(uint) e.key, e.key);
|
|
||||||
parentWindow.dispatchKeyEvent(e);
|
parentWindow.dispatchKeyEvent(e);
|
||||||
},
|
},
|
||||||
(dchar e) {
|
(dchar e) {
|
||||||
|
@ -6369,6 +6425,11 @@ abstract class Layout : Widget {
|
||||||
tabStop = false;
|
tabStop = false;
|
||||||
super(parent);
|
super(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(none)
|
||||||
|
override bool willDraw() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
|
@ -7575,6 +7636,17 @@ class Window : Widget {
|
||||||
win.releaseInputGrab();
|
win.releaseInputGrab();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Sets the window icon which is often seen in title bars and taskbars.
|
||||||
|
|
||||||
|
History:
|
||||||
|
Added April 5, 2022 (dub v10.8)
|
||||||
|
+/
|
||||||
|
@property void icon(MemoryImage icon) {
|
||||||
|
if(win && icon)
|
||||||
|
win.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@scriptable
|
@scriptable
|
||||||
@property bool focused() {
|
@property bool focused() {
|
||||||
|
@ -7640,8 +7712,6 @@ class Window : Widget {
|
||||||
super(p);
|
super(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void actualRedraw() {
|
private void actualRedraw() {
|
||||||
if(recomputeChildLayoutRequired)
|
if(recomputeChildLayoutRequired)
|
||||||
recomputeChildLayoutEntry();
|
recomputeChildLayoutEntry();
|
||||||
|
@ -7664,7 +7734,7 @@ class Window : Widget {
|
||||||
ugh = ugh.parent;
|
ugh = ugh.parent;
|
||||||
}
|
}
|
||||||
auto painter = w.draw(true);
|
auto painter = w.draw(true);
|
||||||
privatePaint(WidgetPainter(painter, this), lox, loy, Rectangle(0, 0, int.max, int.max), false, true);
|
privatePaint(WidgetPainter(painter, this), lox, loy, Rectangle(0, 0, int.max, int.max), false, willDraw());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -7926,11 +7996,21 @@ class Window : Widget {
|
||||||
if(Runtime.args.length)
|
if(Runtime.args.length)
|
||||||
title = Runtime.args[0];
|
title = Runtime.args[0];
|
||||||
}
|
}
|
||||||
win = new SimpleWindow(width, height, title, OpenGlOptions.no, Resizability.allowResizing, WindowTypes.normal, WindowFlags.dontAutoShow);
|
win = new SimpleWindow(width, height, title, OpenGlOptions.no, Resizability.allowResizing, WindowTypes.normal, WindowFlags.dontAutoShow | WindowFlags.managesChildWindowFocus);
|
||||||
|
|
||||||
|
win.setRequestedInputFocus = &this.setRequestedInputFocus;
|
||||||
|
|
||||||
this(win);
|
this(win);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SimpleWindow setRequestedInputFocus() {
|
||||||
|
if(auto fw = cast(NestedChildWindowWidget) focusedWidget) {
|
||||||
|
// sdpyPrintDebugString("heaven");
|
||||||
|
return fw.focusableWindow;
|
||||||
|
}
|
||||||
|
return win;
|
||||||
|
}
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
this(string title, int width = 500, int height = 500) {
|
this(string title, int width = 500, int height = 500) {
|
||||||
this(width, height, title);
|
this(width, height, title);
|
||||||
|
@ -9715,7 +9795,7 @@ class MenuBar : Widget {
|
||||||
Status bars appear at the bottom of a MainWindow.
|
Status bars appear at the bottom of a MainWindow.
|
||||||
They are made out of Parts, with a width and content.
|
They are made out of Parts, with a width and content.
|
||||||
|
|
||||||
They can have multiple parts or be in simple mode. FIXME: implement
|
They can have multiple parts or be in simple mode. FIXME: implement simple mode.
|
||||||
|
|
||||||
|
|
||||||
sb.parts[0].content = "Status bar text!";
|
sb.parts[0].content = "Status bar text!";
|
||||||
|
@ -10809,6 +10889,9 @@ class Button : MouseActivatedWidget {
|
||||||
override int heightStretchiness() { return 3; }
|
override int heightStretchiness() { return 3; }
|
||||||
override int widthStretchiness() { return 3; }
|
override int widthStretchiness() { return 3; }
|
||||||
|
|
||||||
|
version(none)
|
||||||
|
override bool skipPaintCycle() { return true; }
|
||||||
|
|
||||||
/++
|
/++
|
||||||
If true, this button will emit trigger events on double (and other quick events, if added) click events as well as on normal single click events.
|
If true, this button will emit trigger events on double (and other quick events, if added) click events as well as on normal single click events.
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,6 @@ version(linux)
|
||||||
version(Windows)
|
version(Windows)
|
||||||
version=wv2;
|
version=wv2;
|
||||||
|
|
||||||
|
|
||||||
/+
|
/+
|
||||||
SPA mode: put favicon on top level window, no other user controls at top level, links to different domains always open in new window.
|
SPA mode: put favicon on top level window, no other user controls at top level, links to different domains always open in new window.
|
||||||
+/
|
+/
|
||||||
|
@ -64,8 +63,14 @@ class WebViewWidgetBase : NestedChildWindowWidget {
|
||||||
mixin Observable!(string, "title");
|
mixin Observable!(string, "title");
|
||||||
mixin Observable!(string, "url");
|
mixin Observable!(string, "url");
|
||||||
mixin Observable!(string, "status");
|
mixin Observable!(string, "status");
|
||||||
|
|
||||||
|
// not implemented on WV2
|
||||||
mixin Observable!(int, "loadingProgress");
|
mixin Observable!(int, "loadingProgress");
|
||||||
|
|
||||||
|
// not implemented on WV2
|
||||||
|
mixin Observable!(string, "favicon_url");
|
||||||
|
mixin Observable!(MemoryImage, "favicon"); // please note it can be changed to null!
|
||||||
|
|
||||||
abstract void refresh();
|
abstract void refresh();
|
||||||
abstract void back();
|
abstract void back();
|
||||||
abstract void forward();
|
abstract void forward();
|
||||||
|
@ -74,6 +79,8 @@ class WebViewWidgetBase : NestedChildWindowWidget {
|
||||||
abstract void navigate(string url);
|
abstract void navigate(string url);
|
||||||
|
|
||||||
// the url and line are for error reporting purposes. They might be ignored.
|
// the url and line are for error reporting purposes. They might be ignored.
|
||||||
|
// FIXME: add a callback with the reply. this can send a message from the js thread in cef and just ExecuteScript inWV2
|
||||||
|
// FIXME: add AddScriptToExecuteOnDocumentCreated for cef....
|
||||||
abstract void executeJavascript(string code, string url = null, int line = 0);
|
abstract void executeJavascript(string code, string url = null, int line = 0);
|
||||||
// for injecting stuff into the context
|
// for injecting stuff into the context
|
||||||
// abstract void executeJavascriptBeforeEachLoad(string code);
|
// abstract void executeJavascriptBeforeEachLoad(string code);
|
||||||
|
@ -99,6 +106,8 @@ class WebViewWidgetBase : NestedChildWindowWidget {
|
||||||
|
|
||||||
// AddScriptToExecuteOnDocumentCreated
|
// AddScriptToExecuteOnDocumentCreated
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
version(wv2)
|
version(wv2)
|
||||||
class WebViewWidget_WV2 : WebViewWidgetBase {
|
class WebViewWidget_WV2 : WebViewWidgetBase {
|
||||||
private RC!ICoreWebView2 webview_window;
|
private RC!ICoreWebView2 webview_window;
|
||||||
|
@ -107,7 +116,8 @@ class WebViewWidget_WV2 : WebViewWidgetBase {
|
||||||
|
|
||||||
private bool initialized;
|
private bool initialized;
|
||||||
|
|
||||||
this(string url, Widget parent) {
|
this(string url, void delegate(scope OpenNewWindowParams) openNewWindow, Widget parent) {
|
||||||
|
// FIXME: openNewWindow
|
||||||
super(parent);
|
super(parent);
|
||||||
// that ctor sets containerWindow
|
// that ctor sets containerWindow
|
||||||
|
|
||||||
|
@ -207,17 +217,52 @@ class WebViewWidget_WV2 : WebViewWidgetBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
The openInNewWindow delegate is given these params.
|
||||||
|
|
||||||
|
To accept the new window, call
|
||||||
|
|
||||||
|
params.accept(parent_widget);
|
||||||
|
|
||||||
|
Please note, you can force it to replace the current tab
|
||||||
|
by just navigating your current thing to the given url instead
|
||||||
|
of accepting it.
|
||||||
|
|
||||||
|
If you accept with a null widget, it will create a new window
|
||||||
|
but then return null, since the new window is managed by the
|
||||||
|
underlying webview instead of by minigui.
|
||||||
|
|
||||||
|
If you do not call accept, the pop up will be blocked.
|
||||||
|
|
||||||
|
accept returns an instance to the newly created widget, which will
|
||||||
|
be a parent to the widget you passed.
|
||||||
|
|
||||||
|
accept will be called from the gui thread and it MUST not call into
|
||||||
|
any other webview methods. It should only create windows/widgets
|
||||||
|
and set event handlers etc.
|
||||||
|
|
||||||
|
You MUST not escape references to anything in this structure. It
|
||||||
|
is entirely strictly temporary!
|
||||||
|
+/
|
||||||
|
struct OpenNewWindowParams {
|
||||||
|
string url;
|
||||||
|
WebViewWidget delegate(Widget parent, bool enableJavascript = true) accept;
|
||||||
|
}
|
||||||
|
|
||||||
version(cef)
|
version(cef)
|
||||||
class WebViewWidget_CEF : WebViewWidgetBase {
|
class WebViewWidget_CEF : WebViewWidgetBase {
|
||||||
|
/++
|
||||||
|
Create a webview that does not support opening links in new windows.
|
||||||
|
+/
|
||||||
this(string url, Widget parent) {
|
this(string url, Widget parent) {
|
||||||
|
this(url, null, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
this(string url, void delegate(scope OpenNewWindowParams) openNewWindow, Widget parent) {
|
||||||
//semaphore = new Semaphore;
|
//semaphore = new Semaphore;
|
||||||
assert(CefApp.active);
|
assert(CefApp.active);
|
||||||
|
|
||||||
super(parent);
|
this(new MiniguiCefClient(openNewWindow), parent);
|
||||||
|
|
||||||
flushGui();
|
|
||||||
|
|
||||||
mapping[containerWindow.nativeWindowHandle()] = this;
|
|
||||||
|
|
||||||
cef_window_info_t window_info;
|
cef_window_info_t window_info;
|
||||||
window_info.parent_window = containerWindow.nativeWindowHandle;
|
window_info.parent_window = containerWindow.nativeWindowHandle;
|
||||||
|
@ -227,29 +272,77 @@ class WebViewWidget_CEF : WebViewWidgetBase {
|
||||||
cef_browser_settings_t browser_settings;
|
cef_browser_settings_t browser_settings;
|
||||||
browser_settings.size = cef_browser_settings_t.sizeof;
|
browser_settings.size = cef_browser_settings_t.sizeof;
|
||||||
|
|
||||||
client = new MiniguiCefClient();
|
browser_settings.standard_font_family = cef_string_t("DejaVu Sans");
|
||||||
|
browser_settings.fixed_font_family = cef_string_t("DejaVu Sans Mono");
|
||||||
|
browser_settings.serif_font_family = cef_string_t("DejaVu Sans");
|
||||||
|
browser_settings.sans_serif_font_family = cef_string_t("DejaVu Sans");
|
||||||
|
browser_settings.cursive_font_family = cef_string_t("DejaVu Sans");
|
||||||
|
browser_settings.fantasy_font_family = cef_string_t("DejaVu Sans");
|
||||||
|
|
||||||
|
browser_settings.remote_fonts = cef_state_t.STATE_DISABLED;
|
||||||
|
|
||||||
auto got = libcef.browser_host_create_browser(&window_info, client.passable, &cef_url, &browser_settings, null, null);
|
auto got = libcef.browser_host_create_browser(&window_info, client.passable, &cef_url, &browser_settings, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
/+
|
/+
|
||||||
containerWindow.closeQuery = delegate() {
|
~this() {
|
||||||
browserHandle.get_host.close_browser(true);
|
import core.stdc.stdio;
|
||||||
//containerWindow.close();
|
import core.memory;
|
||||||
};
|
printf("CLEANUP %s\n", GC.inFinalizer ? "GC".ptr : "destroy".ptr);
|
||||||
+/
|
}
|
||||||
|
+/
|
||||||
|
|
||||||
|
override void dispose() {
|
||||||
|
// sdpyPrintDebugString("closed");
|
||||||
|
// the window is already gone so too late to do this really....
|
||||||
|
// if(browserHandle) browserHandle.get_host.close_browser(true);
|
||||||
|
|
||||||
|
// sdpyPrintDebugString("DISPOSE");
|
||||||
|
|
||||||
|
if(win && win.nativeWindowHandle())
|
||||||
|
mapping.remove(win.nativeWindowHandle());
|
||||||
|
if(browserWindow)
|
||||||
|
browserMapping.remove(browserWindow);
|
||||||
|
|
||||||
|
.destroy(this); // but this is ok to do some memory management cleanup
|
||||||
|
}
|
||||||
|
|
||||||
|
private this(MiniguiCefClient client, Widget parent) {
|
||||||
|
super(parent);
|
||||||
|
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
flushGui();
|
||||||
|
|
||||||
|
mapping[containerWindow.nativeWindowHandle()] = this;
|
||||||
|
|
||||||
|
|
||||||
|
this.parentWindow.addEventListener((FocusEvent fe) {
|
||||||
|
if(!browserHandle) return;
|
||||||
|
//browserHandle.get_host.set_focus(true);
|
||||||
|
|
||||||
|
executeJavascript("if(window.__arsdPreviouslyFocusedNode) window.__arsdPreviouslyFocusedNode.focus(); window.dispatchEvent(new FocusEvent(\"focus\"));");
|
||||||
|
});
|
||||||
|
this.parentWindow.addEventListener((BlurEvent be) {
|
||||||
|
if(!browserHandle) return;
|
||||||
|
|
||||||
|
executeJavascript("if(document.activeElement) { window.__arsdPreviouslyFocusedNode = document.activeElement; document.activeElement.blur(); } window.dispatchEvent(new FocusEvent(\"blur\"));");
|
||||||
|
});
|
||||||
|
|
||||||
|
bool closeAttempted = false;
|
||||||
|
|
||||||
|
this.parentWindow.addEventListener((scope ClosingEvent ce) {
|
||||||
|
if(!closeAttempted && browserHandle) {
|
||||||
|
browserHandle.get_host.close_browser(true);
|
||||||
|
ce.preventDefault();
|
||||||
|
// sdpyPrintDebugString("closing");
|
||||||
|
}
|
||||||
|
closeAttempted = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private MiniguiCefClient client;
|
private MiniguiCefClient client;
|
||||||
|
|
||||||
/+
|
|
||||||
override void close() {
|
|
||||||
// FIXME: this should prolly be on the onclose event instead
|
|
||||||
mapping.remove[win.nativeWindowHandle()];
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
+/
|
|
||||||
|
|
||||||
override void registerMovementAdditionalWork() {
|
override void registerMovementAdditionalWork() {
|
||||||
if(browserWindow) {
|
if(browserWindow) {
|
||||||
static if(UsingSimpledisplayX11)
|
static if(UsingSimpledisplayX11)
|
||||||
|
@ -258,6 +351,12 @@ class WebViewWidget_CEF : WebViewWidgetBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SimpleWindow browserWindowWrapped;
|
||||||
|
override SimpleWindow focusableWindow() {
|
||||||
|
if(browserWindowWrapped is null && browserWindow)
|
||||||
|
browserWindowWrapped = new SimpleWindow(browserWindow);
|
||||||
|
return browserWindowWrapped;
|
||||||
|
}
|
||||||
|
|
||||||
private NativeWindowHandle browserWindow;
|
private NativeWindowHandle browserWindow;
|
||||||
private RC!cef_browser_t browserHandle;
|
private RC!cef_browser_t browserHandle;
|
||||||
|
@ -311,6 +410,10 @@ version(cef) {
|
||||||
+/
|
+/
|
||||||
void runOnWebView(RC!cef_browser_t browser, void delegate(WebViewWidget) dg) nothrow {
|
void runOnWebView(RC!cef_browser_t browser, void delegate(WebViewWidget) dg) nothrow {
|
||||||
auto wh = cast(NativeWindowHandle) browser.get_host.get_window_handle;
|
auto wh = cast(NativeWindowHandle) browser.get_host.get_window_handle;
|
||||||
|
|
||||||
|
import core.thread;
|
||||||
|
try { thread_attachThis(); } catch(Exception e) {}
|
||||||
|
|
||||||
runInGuiThreadAsync({
|
runInGuiThreadAsync({
|
||||||
if(auto wvp = wh in WebViewWidget.browserMapping) {
|
if(auto wvp = wh in WebViewWidget.browserMapping) {
|
||||||
dg(*wvp);
|
dg(*wvp);
|
||||||
|
@ -321,13 +424,68 @@ version(cef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MiniguiCefLifeSpanHandler : CEF!cef_life_span_handler_t {
|
class MiniguiCefLifeSpanHandler : CEF!cef_life_span_handler_t {
|
||||||
override int on_before_popup(RC!cef_browser_t, RC!cef_frame_t, const(cef_string_utf16_t)*, const(cef_string_utf16_t)*, cef_window_open_disposition_t, int, const(cef_popup_features_t)*, cef_window_info_t*, cef_client_t**, cef_browser_settings_t*, cef_dictionary_value_t**, int*) {
|
private MiniguiCefClient client;
|
||||||
return 0;
|
this(MiniguiCefClient client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
override int on_before_popup(
|
||||||
|
RC!cef_browser_t browser,
|
||||||
|
RC!cef_frame_t frame,
|
||||||
|
const(cef_string_t)* target_url,
|
||||||
|
const(cef_string_t)* target_frame_name,
|
||||||
|
cef_window_open_disposition_t target_disposition,
|
||||||
|
int user_gesture,
|
||||||
|
const(cef_popup_features_t)* popupFeatures,
|
||||||
|
cef_window_info_t* windowInfo,
|
||||||
|
cef_client_t** client,
|
||||||
|
cef_browser_settings_t* settings,
|
||||||
|
cef_dictionary_value_t** extra_info,
|
||||||
|
int* no_javascript_access
|
||||||
|
) {
|
||||||
|
|
||||||
|
if(this.client.openNewWindow is null)
|
||||||
|
return 1; // new windows disabled
|
||||||
|
|
||||||
|
try {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
import core.thread;
|
||||||
|
try { thread_attachThis(); } catch(Exception e) {}
|
||||||
|
|
||||||
|
runInGuiThread({
|
||||||
|
ret = 1;
|
||||||
|
scope WebViewWidget delegate(Widget, bool) o = (parent, enableJavascript) {
|
||||||
|
ret = 0;
|
||||||
|
if(parent !is null) {
|
||||||
|
auto widget = new WebViewWidget_CEF(this.client, parent);
|
||||||
|
(*windowInfo).parent_window = widget.containerWindow.nativeWindowHandle;
|
||||||
|
|
||||||
|
this.client.listOfWidgets ~= widget;
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
this.client.openNewWindow(OpenNewWindowParams("", o));
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
} catch(Exception e) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/+
|
||||||
|
if(!user_gesture)
|
||||||
|
return 1; // if not created by the user, cancel it; basic popup blocking
|
||||||
|
+/
|
||||||
}
|
}
|
||||||
override void on_after_created(RC!cef_browser_t browser) {
|
override void on_after_created(RC!cef_browser_t browser) {
|
||||||
auto handle = cast(NativeWindowHandle) browser.get_host().get_window_handle();
|
auto handle = cast(NativeWindowHandle) browser.get_host().get_window_handle();
|
||||||
auto ptr = browser.passable; // this adds to the refcount until it gets inside
|
auto ptr = browser.passable; // this adds to the refcount until it gets inside
|
||||||
|
|
||||||
|
import core.thread;
|
||||||
|
try { thread_attachThis(); } catch(Exception e) {}
|
||||||
|
|
||||||
// the only reliable key (at least as far as i can tell) is the window handle
|
// the only reliable key (at least as far as i can tell) is the window handle
|
||||||
// so gonna look that up and do the sync mapping that way.
|
// so gonna look that up and do the sync mapping that way.
|
||||||
runInGuiThreadAsync({
|
runInGuiThreadAsync({
|
||||||
|
@ -349,6 +507,16 @@ version(cef) {
|
||||||
wv.browserWindow = handle;
|
wv.browserWindow = handle;
|
||||||
wv.browserHandle = RC!cef_browser_t(ptr);
|
wv.browserHandle = RC!cef_browser_t(ptr);
|
||||||
|
|
||||||
|
wv.browserWindowWrapped = new SimpleWindow(wv.browserWindow);
|
||||||
|
/+
|
||||||
|
XSelectInput(XDisplayConnection.get, handle, EventMask.FocusChangeMask);
|
||||||
|
|
||||||
|
wv.browserWindowWrapped.onFocusChange = (bool got) {
|
||||||
|
import std.format;
|
||||||
|
sdpyPrintDebugString(format("focus change %s %x", got, wv.browserWindowWrapped.impl.window));
|
||||||
|
};
|
||||||
|
+/
|
||||||
|
|
||||||
wv.registerMovementAdditionalWork();
|
wv.registerMovementAdditionalWork();
|
||||||
|
|
||||||
WebViewWidget.browserMapping[handle] = wv;
|
WebViewWidget.browserMapping[handle] = wv;
|
||||||
|
@ -356,7 +524,11 @@ version(cef) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
override int do_close(RC!cef_browser_t browser) {
|
override int do_close(RC!cef_browser_t browser) {
|
||||||
return 0;
|
browser.runOnWebView((wv) {
|
||||||
|
auto bce = new BrowserClosedEvent(wv);
|
||||||
|
bce.dispatch();
|
||||||
|
});
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
override void on_before_close(RC!cef_browser_t browser) {
|
override void on_before_close(RC!cef_browser_t browser) {
|
||||||
/+
|
/+
|
||||||
|
@ -389,7 +561,7 @@ version(cef) {
|
||||||
override int on_file_dialog(RC!(cef_browser_t) browser, cef_file_dialog_mode_t mode, const(cef_string_utf16_t)* title, const(cef_string_utf16_t)* default_file_path, cef_string_list_t accept_filters, int selected_accept_filter, RC!(cef_file_dialog_callback_t) callback) {
|
override int on_file_dialog(RC!(cef_browser_t) browser, cef_file_dialog_mode_t mode, const(cef_string_utf16_t)* title, const(cef_string_utf16_t)* default_file_path, cef_string_list_t accept_filters, int selected_accept_filter, RC!(cef_file_dialog_callback_t) callback) {
|
||||||
try {
|
try {
|
||||||
auto ptr = callback.passable();
|
auto ptr = callback.passable();
|
||||||
runInGuiThreadAsync({
|
browser.runOnWebView((wv) {
|
||||||
getOpenFileName((string name) {
|
getOpenFileName((string name) {
|
||||||
auto callback = RC!cef_file_dialog_callback_t(ptr);
|
auto callback = RC!cef_file_dialog_callback_t(ptr);
|
||||||
auto list = libcef.string_list_alloc();
|
auto list = libcef.string_list_alloc();
|
||||||
|
@ -407,6 +579,64 @@ version(cef) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MiniguiDownloadHandler : CEF!cef_download_handler_t {
|
||||||
|
override void on_before_download(
|
||||||
|
RC!cef_browser_t browser,
|
||||||
|
RC!cef_download_item_t download_item,
|
||||||
|
const(cef_string_t)* suggested_name,
|
||||||
|
RC!cef_before_download_callback_t callback
|
||||||
|
) nothrow
|
||||||
|
{
|
||||||
|
// FIXME: different filename and check if exists for overwrite etc
|
||||||
|
auto fn = cef_string_t(cast(wstring)("/home/me/Downloads/"w ~ suggested_name.str[0..suggested_name.length]));
|
||||||
|
sdpyPrintDebugString(fn.toGC);
|
||||||
|
callback.cont(&fn, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void on_download_updated(
|
||||||
|
RC!cef_browser_t browser,
|
||||||
|
RC!cef_download_item_t download_item,
|
||||||
|
RC!cef_download_item_callback_t cancel
|
||||||
|
) nothrow
|
||||||
|
{
|
||||||
|
sdpyPrintDebugString(download_item.get_percent_complete());
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MiniguiKeyboardHandler : CEF!cef_keyboard_handler_t {
|
||||||
|
override int on_pre_key_event(
|
||||||
|
RC!(cef_browser_t) browser,
|
||||||
|
const(cef_key_event_t)* event,
|
||||||
|
XEvent* osEvent,
|
||||||
|
int* isShortcut
|
||||||
|
) nothrow {
|
||||||
|
/+
|
||||||
|
sdpyPrintDebugString("---pre---");
|
||||||
|
sdpyPrintDebugString(event.focus_on_editable_field);
|
||||||
|
sdpyPrintDebugString(event.windows_key_code);
|
||||||
|
sdpyPrintDebugString(event.modifiers);
|
||||||
|
sdpyPrintDebugString(event.unmodified_character);
|
||||||
|
+/
|
||||||
|
//*isShortcut = 1;
|
||||||
|
return 0; // 1 if handled, which cancels sending it to browser
|
||||||
|
}
|
||||||
|
|
||||||
|
override int on_key_event(
|
||||||
|
RC!(cef_browser_t) browser,
|
||||||
|
const(cef_key_event_t)* event,
|
||||||
|
XEvent* osEvent
|
||||||
|
) nothrow {
|
||||||
|
/+
|
||||||
|
sdpyPrintDebugString("---key---");
|
||||||
|
sdpyPrintDebugString(event.focus_on_editable_field);
|
||||||
|
sdpyPrintDebugString(event.windows_key_code);
|
||||||
|
sdpyPrintDebugString(event.modifiers);
|
||||||
|
+/
|
||||||
|
return 0; // 1 if handled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MiniguiDisplayHandler : CEF!cef_display_handler_t {
|
class MiniguiDisplayHandler : CEF!cef_display_handler_t {
|
||||||
override void on_address_change(RC!(cef_browser_t) browser, RC!(cef_frame_t), const(cef_string_utf16_t)* address) {
|
override void on_address_change(RC!(cef_browser_t) browser, RC!(cef_frame_t), const(cef_string_utf16_t)* address) {
|
||||||
auto url = address.toGC;
|
auto url = address.toGC;
|
||||||
|
@ -420,7 +650,64 @@ version(cef) {
|
||||||
wv.title = t;
|
wv.title = t;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
override void on_favicon_urlchange(RC!(cef_browser_t) browser, cef_string_list_t) {
|
override void on_favicon_urlchange(RC!(cef_browser_t) browser, cef_string_list_t urls) {
|
||||||
|
string url;
|
||||||
|
auto size = libcef.string_list_size(urls);
|
||||||
|
if(size > 0) {
|
||||||
|
cef_string_t str;
|
||||||
|
libcef.string_list_value(urls, 0, &str);
|
||||||
|
url = str.toGC;
|
||||||
|
|
||||||
|
static class Thing : CEF!cef_download_image_callback_t {
|
||||||
|
RC!cef_browser_t browserHandle;
|
||||||
|
this(RC!cef_browser_t browser) nothrow {
|
||||||
|
this.browserHandle = browser;
|
||||||
|
}
|
||||||
|
override void on_download_image_finished(const(cef_string_t)* image_url, int http_status_code, RC!cef_image_t image) nothrow {
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
if(image.getRawPointer is null) {
|
||||||
|
browserHandle.runOnWebView((wv) {
|
||||||
|
wv.favicon = null;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = image.get_as_bitmap(1.0, cef_color_type_t.CEF_COLOR_TYPE_RGBA_8888, cef_alpha_type_t.CEF_ALPHA_TYPE_POSTMULTIPLIED, &width, &height);
|
||||||
|
|
||||||
|
if(data.getRawPointer is null || width == 0 || height == 0) {
|
||||||
|
browserHandle.runOnWebView((wv) {
|
||||||
|
wv.favicon = null;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
auto s = data.get_size();
|
||||||
|
auto buffer = new ubyte[](s);
|
||||||
|
auto got = data.get_data(buffer.ptr, buffer.length, 0);
|
||||||
|
auto slice = buffer[0 .. got];
|
||||||
|
|
||||||
|
auto img = new TrueColorImage (width, height, slice);
|
||||||
|
|
||||||
|
browserHandle.runOnWebView((wv) {
|
||||||
|
wv.favicon = img;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(url.length) {
|
||||||
|
auto callback = new Thing(browser);
|
||||||
|
|
||||||
|
browser.get_host().download_image(&str, true, 16, 0, callback.passable);
|
||||||
|
} else {
|
||||||
|
browser.runOnWebView((wv) {
|
||||||
|
wv.favicon = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
browser.runOnWebView((wv) {
|
||||||
|
wv.favicon_url = url;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
override void on_fullscreen_mode_change(RC!(cef_browser_t) browser, int) {
|
override void on_fullscreen_mode_change(RC!(cef_browser_t) browser, int) {
|
||||||
}
|
}
|
||||||
|
@ -450,16 +737,49 @@ version(cef) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MiniguiFocusHandler : CEF!cef_focus_handler_t {
|
||||||
|
override void on_take_focus(RC!(cef_browser_t) browser, int next) nothrow {
|
||||||
|
// sdpyPrintDebugString("take");
|
||||||
|
}
|
||||||
|
override int on_set_focus(RC!(cef_browser_t) browser, cef_focus_source_t source) nothrow {
|
||||||
|
//browser.runOnWebView((ev) {
|
||||||
|
//sdpyPrintDebugString("setting");
|
||||||
|
//ev.parentWindow.focusedWidget = ev;
|
||||||
|
//});
|
||||||
|
|
||||||
|
return 1; // otherwise, cancel because this bullshit tends to steal focus from other applications and i never, ever, ever want that to happen.
|
||||||
|
// seems to happen because of race condition in it getting a focus event and then stealing the focus from the parent
|
||||||
|
// even though things work fine if i always cancel except
|
||||||
|
// it still keeps the decoration assuming focus though even though it doesn't have it which is kinda fucked up but meh
|
||||||
|
// it also breaks its own pop up menus and drop down boxes to allow this! wtf
|
||||||
|
}
|
||||||
|
override void on_got_focus(RC!(cef_browser_t) browser) nothrow {
|
||||||
|
// sdpyPrintDebugString("got");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MiniguiCefClient : CEF!cef_client_t {
|
class MiniguiCefClient : CEF!cef_client_t {
|
||||||
|
|
||||||
|
WebViewWidget_CEF[] listOfWidgets;
|
||||||
|
|
||||||
|
void delegate(scope OpenNewWindowParams) openNewWindow;
|
||||||
|
|
||||||
MiniguiCefLifeSpanHandler lsh;
|
MiniguiCefLifeSpanHandler lsh;
|
||||||
MiniguiLoadHandler loadHandler;
|
MiniguiLoadHandler loadHandler;
|
||||||
MiniguiDialogHandler dialogHandler;
|
MiniguiDialogHandler dialogHandler;
|
||||||
MiniguiDisplayHandler displayHandler;
|
MiniguiDisplayHandler displayHandler;
|
||||||
this() {
|
MiniguiDownloadHandler downloadHandler;
|
||||||
lsh = new MiniguiCefLifeSpanHandler();
|
MiniguiKeyboardHandler keyboardHandler;
|
||||||
|
MiniguiFocusHandler focusHandler;
|
||||||
|
this(void delegate(scope OpenNewWindowParams) openNewWindow) {
|
||||||
|
this.openNewWindow = openNewWindow;
|
||||||
|
lsh = new MiniguiCefLifeSpanHandler(this);
|
||||||
loadHandler = new MiniguiLoadHandler();
|
loadHandler = new MiniguiLoadHandler();
|
||||||
dialogHandler = new MiniguiDialogHandler();
|
dialogHandler = new MiniguiDialogHandler();
|
||||||
displayHandler = new MiniguiDisplayHandler();
|
displayHandler = new MiniguiDisplayHandler();
|
||||||
|
downloadHandler = new MiniguiDownloadHandler();
|
||||||
|
keyboardHandler = new MiniguiKeyboardHandler();
|
||||||
|
focusHandler = new MiniguiFocusHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
override cef_audio_handler_t* get_audio_handler() {
|
override cef_audio_handler_t* get_audio_handler() {
|
||||||
|
@ -475,7 +795,7 @@ version(cef) {
|
||||||
return displayHandler.returnable;
|
return displayHandler.returnable;
|
||||||
}
|
}
|
||||||
override cef_download_handler_t* get_download_handler() {
|
override cef_download_handler_t* get_download_handler() {
|
||||||
return null;
|
return downloadHandler.returnable;
|
||||||
}
|
}
|
||||||
override cef_drag_handler_t* get_drag_handler() {
|
override cef_drag_handler_t* get_drag_handler() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -484,7 +804,7 @@ version(cef) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
override cef_focus_handler_t* get_focus_handler() {
|
override cef_focus_handler_t* get_focus_handler() {
|
||||||
return null;
|
return focusHandler.returnable;
|
||||||
}
|
}
|
||||||
override cef_jsdialog_handler_t* get_jsdialog_handler() {
|
override cef_jsdialog_handler_t* get_jsdialog_handler() {
|
||||||
// needed for alert etc.
|
// needed for alert etc.
|
||||||
|
@ -492,7 +812,7 @@ version(cef) {
|
||||||
}
|
}
|
||||||
override cef_keyboard_handler_t* get_keyboard_handler() {
|
override cef_keyboard_handler_t* get_keyboard_handler() {
|
||||||
// this can handle keyboard shortcuts etc
|
// this can handle keyboard shortcuts etc
|
||||||
return null;
|
return keyboardHandler.returnable;
|
||||||
}
|
}
|
||||||
override cef_life_span_handler_t* get_life_span_handler() {
|
override cef_life_span_handler_t* get_life_span_handler() {
|
||||||
return lsh.returnable;
|
return lsh.returnable;
|
||||||
|
@ -520,3 +840,19 @@ version(cef) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BrowserClosedEvent : Event {
|
||||||
|
enum EventString = "browserclosed";
|
||||||
|
|
||||||
|
this(Widget target) { super(EventString, target); }
|
||||||
|
override bool cancelable() const { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/+
|
||||||
|
pragma(mangle, "_ZN12CefWindowX115FocusEv")
|
||||||
|
//pragma(mangle, "_ZN3x116XProto13SetInputFocusERKNS_20SetInputFocusRequestE")
|
||||||
|
extern(C++)
|
||||||
|
export void _ZN12CefWindowX115FocusEv() {
|
||||||
|
sdpyPrintDebugString("OVERRIDDEN");
|
||||||
|
}
|
||||||
|
+/
|
||||||
|
|
|
@ -2897,8 +2897,10 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
||||||
private Image secret_icon_inner;
|
private Image secret_icon_inner;
|
||||||
|
|
||||||
|
|
||||||
/// Set the icon that is seen in the title bar or taskbar, etc., for the user.
|
/// Set the icon that is seen in the title bar or taskbar, etc., for the user. If passed `null`, does nothing.
|
||||||
@property void icon(MemoryImage icon) {
|
@property void icon(MemoryImage icon) {
|
||||||
|
if(icon is null)
|
||||||
|
return;
|
||||||
auto tci = icon.getAsTrueColorImage();
|
auto tci = icon.getAsTrueColorImage();
|
||||||
version(Windows) {
|
version(Windows) {
|
||||||
winIcon = new WindowsIcon(icon);
|
winIcon = new WindowsIcon(icon);
|
||||||
|
@ -6795,8 +6797,8 @@ struct SyntheticInput {
|
||||||
input.mi.dwFlags = MOUSEEVENTF_WHEEL;
|
input.mi.dwFlags = MOUSEEVENTF_WHEEL;
|
||||||
input.mi.mouseData = button == MouseButton.wheelUp ? 120 : -120;
|
input.mi.mouseData = button == MouseButton.wheelUp ? 120 : -120;
|
||||||
break;
|
break;
|
||||||
case MouseButton.backButton: throw new NotYetImplementedException(); break;
|
case MouseButton.backButton: throw new NotYetImplementedException();
|
||||||
case MouseButton.forwardButton: throw new NotYetImplementedException(); break;
|
case MouseButton.forwardButton: throw new NotYetImplementedException();
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3628,6 +3628,12 @@ version(Posix) {
|
||||||
|
|
||||||
argv[args.length] = null;
|
argv[args.length] = null;
|
||||||
|
|
||||||
|
termios info;
|
||||||
|
ubyte[128] hack; // jic that druntime definition is still wrong
|
||||||
|
tcgetattr(master, &info);
|
||||||
|
info.c_cc[VERASE] = '\b';
|
||||||
|
tcsetattr(master, TCSANOW, &info);
|
||||||
|
|
||||||
core.sys.posix.unistd.execv(argv[0], argv);
|
core.sys.posix.unistd.execv(argv[0], argv);
|
||||||
} else {
|
} else {
|
||||||
childrenAlive = 1;
|
childrenAlive = 1;
|
||||||
|
|
|
@ -1061,12 +1061,12 @@ abstract class CEF(Base) {
|
||||||
}
|
}
|
||||||
private Inner inner;
|
private Inner inner;
|
||||||
|
|
||||||
this() {
|
this() nothrow {
|
||||||
if(!__ctfe) construct();
|
if(!__ctfe) construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ONLY call this if you did a ctfe construction
|
// ONLY call this if you did a ctfe construction
|
||||||
void construct() {
|
void construct() nothrow {
|
||||||
assert(inner.c.base.size == 0);
|
assert(inner.c.base.size == 0);
|
||||||
|
|
||||||
import core.memory;
|
import core.memory;
|
||||||
|
@ -1234,6 +1234,7 @@ struct RC(Base) {
|
||||||
if(inner is null) return;
|
if(inner is null) return;
|
||||||
inner.base.release(&inner.base);
|
inner.base.release(&inner.base);
|
||||||
inner = null;
|
inner = null;
|
||||||
|
//sdpyPrintDebugString("omg release");
|
||||||
}
|
}
|
||||||
bool opCast(T:bool)() nothrow {
|
bool opCast(T:bool)() nothrow {
|
||||||
return inner !is null;
|
return inner !is null;
|
||||||
|
|
Loading…
Reference in New Issue