mirror of https://github.com/adamdruppe/arsd.git
fixes
This commit is contained in:
parent
0a3995a14d
commit
0367ac70e7
41
cgi.d
41
cgi.d
|
@ -3354,10 +3354,15 @@ bool tryAddonServers(string[] args) {
|
|||
printf("Add-on servers not compiled in.\n");
|
||||
return true;
|
||||
case "--timer-server":
|
||||
try {
|
||||
version(with_addon_servers)
|
||||
runTimerServer();
|
||||
else
|
||||
printf("Add-on servers not compiled in.\n");
|
||||
} catch(Throwable t) {
|
||||
import std.file;
|
||||
std.file.write("/tmp/timer-exception", t.toString);
|
||||
}
|
||||
return true;
|
||||
case "--timed-jobs":
|
||||
import core.demangle;
|
||||
|
@ -5985,7 +5990,7 @@ void startAddonServer()(string arg) {
|
|||
import core.sys.posix.unistd;
|
||||
pid_t pid;
|
||||
const(char)*[16] args;
|
||||
args[0] = "ARSD_CGI_WEBSOCKET_SERVER";
|
||||
args[0] = "ARSD_CGI_ADDON_SERVER";
|
||||
args[1] = arg.ptr;
|
||||
posix_spawn(&pid, "/proc/self/exe",
|
||||
null,
|
||||
|
@ -6459,6 +6464,13 @@ mixin template ImplementRpcClientInterface(T, string serverPath, string cmdArg)
|
|||
|
||||
version(Posix) {{
|
||||
auto ret = send(connectionHandle, sendable.ptr, sendable.length, 0);
|
||||
|
||||
if(ret == -1) {
|
||||
throw new Exception("send returned -1, errno: " ~ to!string(errno));
|
||||
} else if(ret == 0) {
|
||||
throw new Exception("Connection to addon server lost");
|
||||
} if(ret < sendable.length)
|
||||
throw new Exception("Send failed to send all");
|
||||
assert(ret == sendable.length);
|
||||
}} // FIXME Windows impl
|
||||
|
||||
|
@ -6514,6 +6526,7 @@ void dispatchRpcServer(Interface, Class)(Class this_, ubyte[] data, int fd) if(i
|
|||
|
||||
int dataLocation;
|
||||
ubyte[] grab(int sz) {
|
||||
if(sz == 0) assert(0);
|
||||
auto d = data[dataLocation .. dataLocation + sz];
|
||||
dataLocation += sz;
|
||||
return d;
|
||||
|
@ -6547,7 +6560,13 @@ void dispatchRpcServer(Interface, Class)(Class this_, ubyte[] data, int fd) if(i
|
|||
|
||||
version(Posix) {
|
||||
auto r = send(fd, sendable.ptr, sendable.length, 0);
|
||||
assert(r == sendable.length);
|
||||
if(r == -1) {
|
||||
throw new Exception("send returned -1, errno: " ~ to!string(errno));
|
||||
} else if(r == 0) {
|
||||
throw new Exception("Connection to addon client lost");
|
||||
} if(r < sendable.length)
|
||||
throw new Exception("Send failed to send all");
|
||||
|
||||
} // FIXME Windows impl
|
||||
}
|
||||
break sw;
|
||||
|
@ -7016,7 +7035,9 @@ final class ScheduledJobServerImplementation : ScheduledJobServer, EventIoServer
|
|||
if(fd == -1)
|
||||
throw new Exception("fd timer create failed");
|
||||
|
||||
auto job = Job(executable, func, args, fd, nj);
|
||||
foreach(ref arg; args)
|
||||
arg = arg.idup;
|
||||
auto job = Job(executable.idup, func.idup, .dup(args), fd, nj);
|
||||
|
||||
itimerspec value;
|
||||
value.it_value.tv_sec = when;
|
||||
|
@ -7030,8 +7051,11 @@ final class ScheduledJobServerImplementation : ScheduledJobServer, EventIoServer
|
|||
|
||||
auto op = allocateIoOp(fd, IoOp.Read, 16, (IoOp* op, int fd) {
|
||||
jobs.remove(nj);
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, null);
|
||||
close(fd);
|
||||
|
||||
spawnProcess([job.executable, "--timed-job", job.func] ~ args);
|
||||
|
||||
spawnProcess([job.executable, "--timed-job", job.func] ~ job.args);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
@ -7055,6 +7079,8 @@ final class ScheduledJobServerImplementation : ScheduledJobServer, EventIoServer
|
|||
if(job is null)
|
||||
return;
|
||||
|
||||
jobs.remove(jobId);
|
||||
|
||||
version(linux) {
|
||||
import core.sys.linux.timerfd;
|
||||
import core.sys.linux.epoll;
|
||||
|
@ -7453,6 +7479,13 @@ void runAddonServer(EIS)(string localListenerName, EIS eis) if(is(EIS : EventIoS
|
|||
import core.sys.posix.signal;
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
static extern(C) void sigchldhandler(int) {
|
||||
int status;
|
||||
import w = core.sys.posix.sys.wait;
|
||||
w.wait(&status);
|
||||
}
|
||||
signal(SIGCHLD, &sigchldhandler);
|
||||
|
||||
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if(sock == -1)
|
||||
throw new Exception("socket " ~ to!string(errno));
|
||||
|
|
3
dub.json
3
dub.json
|
@ -477,7 +477,8 @@
|
|||
"targetType": "library",
|
||||
"sourceFiles": ["libssh2.d"],
|
||||
"importPaths": ["."],
|
||||
"libs": ["ssh2"],
|
||||
"libs-posix": ["ssh2"],
|
||||
"libs-windows": ["libssh2"],
|
||||
"dflags": ["-mv=arsd.libssh2=libssh2.d"]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
See [imageResize] for the main function, all others are lower level if you need
|
||||
more control.
|
||||
|
||||
Note that this focuses more on quality than speed. You can tweak the `filterScale`
|
||||
argument to speed things up at the expense of quality though (lower number = faster).
|
||||
|
||||
|
||||
Authors:
|
||||
Originally written in C by Rich Geldreich, ported to D by ketmar.
|
||||
|
@ -78,6 +81,29 @@ public int imageResizeFindFilter (const(char)[] name, const(char)[] defaultFilte
|
|||
return res;
|
||||
}
|
||||
|
||||
/++
|
||||
Calculates a new size that fits inside the maximums while keeping the original aspect ratio.
|
||||
|
||||
History:
|
||||
Added March 18, 2021 (dub v9.4)
|
||||
+/
|
||||
public Size calculateSizeKeepingAspectRatio(int currentWidth, int currentHeight, int maxWidth, int maxHeight) {
|
||||
if(currentWidth <= maxWidth && currentHeight <= maxHeight)
|
||||
return Size(currentWidth, currentHeight);
|
||||
|
||||
float shrinkage = 1.0;
|
||||
|
||||
if(currentWidth > maxWidth) {
|
||||
shrinkage = cast(float) maxWidth / currentWidth;
|
||||
}
|
||||
if(currentHeight > maxHeight) {
|
||||
auto shrinkage2 = cast(float) maxHeight / currentHeight;
|
||||
if(shrinkage2 < shrinkage)
|
||||
shrinkage = shrinkage2;
|
||||
}
|
||||
|
||||
return Size(cast(int) (currentWidth * shrinkage), cast(int) (currentHeight * shrinkage));
|
||||
}
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////////// //
|
||||
/// Resize image.
|
||||
|
|
104
libssh2.d
104
libssh2.d
|
@ -57,7 +57,7 @@ void main() {
|
|||
|
||||
char[1024] buffer;
|
||||
again:
|
||||
auto got = libssh2_sftp_read(handle, buffer.ptr, cast(int) buffer.length);
|
||||
auto got = libssh2_sftp_read(handle, buffer.ptr, buffer.length);
|
||||
|
||||
import std.stdio;
|
||||
writeln(buffer[0 .. got]);
|
||||
|
@ -266,9 +266,87 @@ extern(C) {
|
|||
|
||||
ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen);
|
||||
ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count);
|
||||
|
||||
enum LIBSSH2_SFTP_ATTR {
|
||||
SIZE = 0x00000001,
|
||||
UIDGID = 0x00000002,
|
||||
PERMISSIONS = 0x00000004,
|
||||
ACMODTIME = 0x00000008,
|
||||
EXTENDED = 0x80000000,
|
||||
}
|
||||
|
||||
struct LIBSSH2_SFTP_ATTRIBUTES {
|
||||
c_ulong flags; // see LIBSSH2_SFTP_ATTR
|
||||
|
||||
ulong filesize;
|
||||
c_ulong uid, gid;
|
||||
c_ulong permissions;
|
||||
c_ulong atime, mtime;
|
||||
}
|
||||
|
||||
int libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *handle,
|
||||
char *buffer, size_t buffer_maxlen,
|
||||
char *longentry, size_t longentry_maxlen, // longentry is just a user-friendly display
|
||||
LIBSSH2_SFTP_ATTRIBUTES *attrs);
|
||||
int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp,
|
||||
const char *path,
|
||||
uint,
|
||||
int stat_type,
|
||||
LIBSSH2_SFTP_ATTRIBUTES *attrs);
|
||||
int libssh2_sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle,
|
||||
LIBSSH2_SFTP_STATVFS *st);
|
||||
int libssh2_sftp_statvfs(LIBSSH2_SFTP *sftp,
|
||||
const char *path,
|
||||
size_t path_len,
|
||||
LIBSSH2_SFTP_STATVFS *st);
|
||||
int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp,
|
||||
const char *path,
|
||||
uint);
|
||||
int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp,
|
||||
const char *path,
|
||||
uint, c_long mode);
|
||||
int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp,
|
||||
const char *filename,
|
||||
uint);
|
||||
int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp,
|
||||
const char *path,
|
||||
uint,
|
||||
char *target,
|
||||
uint,
|
||||
int link_type);
|
||||
int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp,
|
||||
const char *source_filename,
|
||||
uint,
|
||||
const char *dest_filename,
|
||||
uint,
|
||||
c_long flags);
|
||||
|
||||
struct LIBSSH2_SFTP_STATVFS {
|
||||
ulong f_bsize; /* file system block size */
|
||||
ulong f_frsize; /* fragment size */
|
||||
ulong f_blocks; /* size of fs in f_frsize units */
|
||||
ulong f_bfree; /* # free blocks */
|
||||
ulong f_bavail; /* # free blocks for non-root */
|
||||
ulong f_files; /* # inodes */
|
||||
ulong f_ffree; /* # free inodes */
|
||||
ulong f_favail; /* # free inodes for non-root */
|
||||
ulong f_fsid; /* file system ID */
|
||||
ulong f_flag; /* mount flags */
|
||||
ulong f_namemax; /* maximum filename length */
|
||||
}
|
||||
|
||||
|
||||
/* end sftp */
|
||||
|
||||
int libssh2_userauth_password(LIBSSH2_SESSION*, const char* username, const char* password);
|
||||
int libssh2_userauth_password_ex(LIBSSH2_SESSION *session,
|
||||
const char *username,
|
||||
uint username_len,
|
||||
const char *password,
|
||||
uint password_len,
|
||||
void* passwd_change_cb);
|
||||
//LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)));
|
||||
|
||||
//int libssh2_userauth_password(LIBSSH2_SESSION*, const char* username, const char* password);
|
||||
int libssh2_userauth_publickey_fromfile_ex(
|
||||
LIBSSH2_SESSION* session,
|
||||
const char *username,
|
||||
|
@ -277,6 +355,15 @@ extern(C) {
|
|||
const char *privatekey,
|
||||
const char *passphrase);
|
||||
|
||||
struct LIBSSH2_LISTENER {}
|
||||
LIBSSH2_LISTENER * libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, const char *host,
|
||||
int port, int *bound_port,
|
||||
int queue_maxsize);
|
||||
int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener);
|
||||
LIBSSH2_CHANNEL * libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener);
|
||||
LIBSSH2_CHANNEL * libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, const char *host,
|
||||
int port, const char *shost, int sport);
|
||||
|
||||
struct LIBSSH2_CHANNEL {}
|
||||
LIBSSH2_CHANNEL* libssh2_channel_open_ex(
|
||||
LIBSSH2_SESSION *session,
|
||||
|
@ -352,4 +439,17 @@ extern(C) {
|
|||
enum LIBSSH2_FLAG_SIGPIPE = 1;
|
||||
enum LIBSSH2_FLAG_COMPRESS = 2;
|
||||
|
||||
|
||||
int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel,
|
||||
int single_connection,
|
||||
const char *auth_proto,
|
||||
const char *auth_cookie,
|
||||
int screen_number);
|
||||
|
||||
|
||||
int libssh2_channel_get_exit_status(LIBSSH2_CHANNEL* channel);
|
||||
int libssh2_channel_get_exit_signal(LIBSSH2_CHANNEL *channel, char **exitsignal, size_t *exitsignal_len, char **errmsg, size_t *errmsg_len, char **langtag, size_t *langtag_len);
|
||||
|
||||
int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel);
|
||||
|
||||
}
|
||||
|
|
139
rss.d
139
rss.d
|
@ -23,20 +23,72 @@ struct Feed {
|
|||
static struct Item {
|
||||
string title; ///
|
||||
string link; ///
|
||||
string description; ///
|
||||
string author; ///
|
||||
string publicationDate; ///
|
||||
string lastUpdatedDate; ///
|
||||
string description; /// could be html or text!
|
||||
string author; /// Typical format: email (name)
|
||||
string publicationDate; /// the format is 2005-07-31T12:29:29Z
|
||||
string lastUpdatedDate; /// the format is 2005-07-31T12:29:29Z
|
||||
string guid; ///
|
||||
|
||||
string enclosureUri; ///
|
||||
string enclosureType; ///
|
||||
string enclosureType; /// a mime type
|
||||
string enclosureSize; ///
|
||||
}
|
||||
|
||||
Item[] items; ///
|
||||
}
|
||||
|
||||
/+
|
||||
import arsd.cgi;
|
||||
mixin GenericMain!handler;
|
||||
void handler(Cgi cgi) {
|
||||
cgi.setResponseContentType("application/atom+xml");
|
||||
cgi.write(feedToAtom(parseFeed(Document.fromUrl("http://dpldocs.info/this-week-in-d/twid.rss", true).root)).toString);
|
||||
}
|
||||
+/
|
||||
|
||||
/++
|
||||
Turns a generic feed back into an Atom document.
|
||||
|
||||
History:
|
||||
Added March 18, 2021
|
||||
+/
|
||||
XmlDocument feedToAtom(Feed feed) {
|
||||
auto document = new XmlDocument(`<feed xmlns="http://www.w3.org/2005/Atom"></feed>`);
|
||||
document.root.addChild("title", feed.title);
|
||||
document.root.addChild("subtitle", feed.description);
|
||||
document.root.addChild("updated", feed.lastUpdated);
|
||||
|
||||
foreach(item; feed.items) {
|
||||
auto entry = document.root.addChild("entry");
|
||||
entry.addChild("title", item.title);
|
||||
entry.addChild("link").setAttribute("href", item.link);
|
||||
if(item.enclosureUri.length)
|
||||
entry.addChild("link").
|
||||
setAttribute("rel", "enclosure").
|
||||
setAttribute("href", item.enclosureUri).
|
||||
setAttribute("length", item.enclosureSize).
|
||||
setAttribute("type", item.enclosureType);
|
||||
entry.addChild("id", item.guid);
|
||||
entry.addChild("published", item.publicationDate);
|
||||
entry.addChild("updated", item.lastUpdatedDate);
|
||||
entry.addChild("content", item.description).setAttribute("type", "html"); // or summary? idk
|
||||
if(item.author.length) {
|
||||
auto author = entry.addChild("author");
|
||||
import std.string;
|
||||
auto idx = item.author.indexOf("(");
|
||||
if(idx == -1) {
|
||||
author.addChild("email", item.author);
|
||||
} else {
|
||||
if(item.author.length > idx + 2)
|
||||
author.addChild("name", item.author[idx + 1 .. $-1]);
|
||||
author.addChild("email", item.author[0 .. idx -1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
///
|
||||
enum FeedType {
|
||||
unknown, ///
|
||||
|
@ -100,7 +152,7 @@ struct RssChannel {
|
|||
Feed f;
|
||||
f.title = this.title;
|
||||
f.description = this.description; // FIXME text vs html?
|
||||
f.lastUpdated = this.lastBuildDate; // FIXME: normalize format rss uses "Mon, 18 Nov 2019 12:00:00 GMT"
|
||||
f.lastUpdated = this.lastBuildDate.rssDateToAtom;
|
||||
|
||||
foreach(item; items) {
|
||||
Feed.Item fi;
|
||||
|
@ -109,7 +161,7 @@ struct RssChannel {
|
|||
fi.link = item.link;
|
||||
fi.description = item.description; // FIXME: try to normalize text vs html
|
||||
fi.author = item.author; // FIXME
|
||||
fi.publicationDate = item.pubDate; // FIXME
|
||||
fi.lastUpdatedDate = fi.publicationDate = item.pubDate.rssDateToAtom;
|
||||
fi.guid = item.guid;
|
||||
//fi.lastUpdatedDate; // not available i think
|
||||
|
||||
|
@ -258,16 +310,21 @@ struct AtomFeed {
|
|||
|
||||
feed.title = this.title;
|
||||
feed.description = this.subtitle;
|
||||
feed.lastUpdated = this.updated; // FIXME: normalize the format is 2005-07-31T12:29:29Z
|
||||
feed.lastUpdated = this.updated;
|
||||
|
||||
foreach(entry; this.entries) {
|
||||
Feed.Item item;
|
||||
|
||||
item.title = entry.title;
|
||||
item.link = entry.link;
|
||||
item.description = entry.summary.html.length ? entry.summary.html : entry.summary.text; // FIXME
|
||||
item.author = entry.author.email; // FIXME normalize; RSS does "email (name)"
|
||||
item.publicationDate = entry.published; // FIXME the format is 2005-07-31T12:29:29Z
|
||||
if(entry.content.html.length || entry.content.text.length)
|
||||
item.description = entry.content.html.length ? entry.content.html : entry.content.text; // FIXME
|
||||
else
|
||||
item.description = entry.summary.html.length ? entry.summary.html : entry.summary.text; // FIXME
|
||||
item.author = entry.author.email;
|
||||
if(entry.author.name.length)
|
||||
item.author ~= " (" ~ entry.author.name ~ ")";
|
||||
item.publicationDate = entry.published;
|
||||
item.lastUpdatedDate = entry.updated;
|
||||
item.guid = entry.id;
|
||||
|
||||
|
@ -384,6 +441,60 @@ AtomFeed parseAtom(string s) {
|
|||
return parseAtom(document.root);
|
||||
}
|
||||
|
||||
string rssDateToAtom(string d) {
|
||||
auto orig = d;
|
||||
if(d.length < 22 || d[3] != ',')
|
||||
return orig; // doesn't appear to be the right format
|
||||
d = d[5 .. $];
|
||||
|
||||
import std.conv;
|
||||
auto day = parse!int(d);
|
||||
if(d.length == 0 || d[0] != ' ')
|
||||
return orig;
|
||||
d = d[1 .. $];
|
||||
|
||||
if(d.length < 4)
|
||||
return orig;
|
||||
|
||||
int month;
|
||||
|
||||
string months = "JanFebMarAprMayJunJulAugSepOctNovDec";
|
||||
foreach(i; 0 .. 12) {
|
||||
if(months[i * 3 .. i * 3 + 3] == d[0 .. 3]) {
|
||||
month = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
d = d[4 .. $];
|
||||
|
||||
auto year = parse!int(d);
|
||||
|
||||
if(d.length == 0 || d[0] != ' ')
|
||||
return orig;
|
||||
d = d[1 .. $];
|
||||
|
||||
auto hour = parse!int(d);
|
||||
|
||||
if(d.length == 0 || d[0] != ':')
|
||||
return orig;
|
||||
d = d[1 .. $];
|
||||
|
||||
auto minute = parse!int(d);
|
||||
|
||||
if(d.length == 0 || d[0] != ':')
|
||||
return orig;
|
||||
d = d[1 .. $];
|
||||
|
||||
auto second = parse!int(d);
|
||||
|
||||
import std.format;
|
||||
return format("%04d-%02d-%02dT%02d:%02d:%02dZ", year, month, day, hour, minute, second);
|
||||
}
|
||||
unittest {
|
||||
assert(rssDateToAtom("Mon, 18 Nov 2019 12:05:44 GMT") == "2019-11-18T12:05:44Z");
|
||||
}
|
||||
|
||||
unittest {
|
||||
|
||||
auto test1 = `<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
|
@ -647,6 +758,10 @@ auto testAtom1 = `<?xml version="1.0" encoding="utf-8"?>
|
|||
assert(e.entries[0].summary.html.length == 0);
|
||||
assert(e.entries[0].content.text.length == 0);
|
||||
assert(e.entries[0].content.html.length > 10);
|
||||
|
||||
auto gf = e.toGenericFeed();
|
||||
|
||||
assert(gf.items[0].lastUpdatedDate == "2003-12-13T18:30:02Z");
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -702,6 +817,8 @@ auto testAtom1 = `<?xml version="1.0" encoding="utf-8"?>
|
|||
|
||||
auto gf = e.toGenericFeed();
|
||||
assert(gf.items[0].link == "https://www.nytimes.com/2019/12/06/world/europe/france-pension-strike-macron.html?emc=rss&partner=rss", e.items[0].link);
|
||||
|
||||
assert(gf.items[0].publicationDate == "2019-12-06T18:02:13Z");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -198,6 +198,10 @@ interface->SetProgressValue(hwnd, 40, 100);
|
|||
its specific implementation. If you disagree with how I did something, please contact me so we
|
||||
can discuss it!
|
||||
|
||||
$(H2 Using with fibers)
|
||||
|
||||
simpledisplay can be used with [core.thread.Fiber], but be warned many of the functions can use a significant amount of stack space. I recommend at least 64 KB stack for each fiber (just set through the second argument to Fiber's constructor).
|
||||
|
||||
Examples:
|
||||
|
||||
$(H3 Event-example)
|
||||
|
@ -1858,7 +1862,6 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
|||
version(Windows)
|
||||
ShowWindow(impl.hwnd, SW_MAXIMIZE);
|
||||
else version(X11) {
|
||||
// I actually could set both at once...
|
||||
setNetWmStateAtom(this.impl.window, GetAtom!("_NET_WM_STATE_MAXIMIZED_VERT", false)(XDisplayConnection.get), true, GetAtom!("_NET_WM_STATE_MAXIMIZED_HORZ", false)(XDisplayConnection.get));
|
||||
|
||||
// also note _NET_WM_STATE_FULLSCREEN
|
||||
|
@ -1866,6 +1869,21 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
|||
|
||||
}
|
||||
|
||||
private bool _fullscreen;
|
||||
|
||||
/// not fully implemented but planned for a future release
|
||||
void fullscreen(bool yes) {
|
||||
version(X11)
|
||||
setNetWmStateAtom(this.impl.window, GetAtom!("_NET_WM_STATE_FULLSCREEN", false)(XDisplayConnection.get), yes);
|
||||
|
||||
_fullscreen = yes;
|
||||
|
||||
}
|
||||
|
||||
bool fullscreen() {
|
||||
return _fullscreen;
|
||||
}
|
||||
|
||||
/++
|
||||
Note: only implemented on Windows. No-op on other platforms. You may want to use [hide] instead.
|
||||
|
||||
|
@ -1972,12 +1990,14 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
|||
width and height are actually changed.
|
||||
+/
|
||||
void resize(int w, int h) {
|
||||
if(!_closed && _fullscreen) fullscreen = false;
|
||||
version(OSXCocoa) throw new NotYetImplementedException(); else
|
||||
if (!_closed) impl.resize(w, h);
|
||||
}
|
||||
|
||||
/// Move and resize window (this can be faster and more visually pleasant than doing it separately).
|
||||
void moveResize (int x, int y, int w, int h) {
|
||||
if(!_closed && _fullscreen) fullscreen = false;
|
||||
version(OSXCocoa) throw new NotYetImplementedException(); else
|
||||
if (!_closed) impl.moveResize(x, y, w, h);
|
||||
}
|
||||
|
@ -18044,7 +18064,28 @@ struct DropPackage {
|
|||
auto selectionAtom = GetAtom!"XdndSelection"(display);
|
||||
auto best = format;
|
||||
|
||||
class X11GetSelectionHandler_Drop : X11GetSelectionHandler {
|
||||
static class X11GetSelectionHandler_Drop : X11GetSelectionHandler {
|
||||
|
||||
XDisplay* display;
|
||||
Atom selectionAtom;
|
||||
DraggableData.FormatId best;
|
||||
DraggableData.FormatId format;
|
||||
void delegate(scope ubyte[] data) dg;
|
||||
DragAndDropAction acceptedAction;
|
||||
Window sourceWindow;
|
||||
SimpleWindow win;
|
||||
this(XDisplay* display, SimpleWindow win, Window sourceWindow, DraggableData.FormatId format, Atom selectionAtom, DraggableData.FormatId best, void delegate(scope ubyte[] data) dg, DragAndDropAction acceptedAction) {
|
||||
this.display = display;
|
||||
this.win = win;
|
||||
this.sourceWindow = sourceWindow;
|
||||
this.format = format;
|
||||
this.selectionAtom = selectionAtom;
|
||||
this.best = best;
|
||||
this.dg = dg;
|
||||
this.acceptedAction = acceptedAction;
|
||||
}
|
||||
|
||||
|
||||
mixin X11GetSelectionHandler_Basics;
|
||||
|
||||
void handleData(Atom target, in ubyte[] data) {
|
||||
|
@ -18063,7 +18104,7 @@ struct DropPackage {
|
|||
xclient.format = 32;
|
||||
xclient.data.l[0] = win.impl.window;
|
||||
xclient.data.l[1] = 1; // drop successful
|
||||
xclient.data.l[2] = GetAtom!"XdndActionCopy"(display); // FIXME: actual accepted action
|
||||
xclient.data.l[2] = dndActionAtom(display, acceptedAction);
|
||||
|
||||
XSendEvent(
|
||||
display,
|
||||
|
@ -18072,6 +18113,8 @@ struct DropPackage {
|
|||
EventMask.NoEventMask,
|
||||
cast(XEvent*) &xclient
|
||||
);
|
||||
|
||||
XFlush(display);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18097,7 +18140,7 @@ struct DropPackage {
|
|||
}
|
||||
}
|
||||
|
||||
win.impl.getSelectionHandlers[selectionAtom] = new X11GetSelectionHandler_Drop();
|
||||
win.impl.getSelectionHandlers[selectionAtom] = new X11GetSelectionHandler_Drop(display, win, sourceWindow, format, selectionAtom, best, dg, acceptedAction);
|
||||
|
||||
XConvertSelection(display, selectionAtom, best, GetAtom!("SDD_DATA", true)(display), win.impl.window, dataTimestamp);
|
||||
|
||||
|
@ -18197,8 +18240,10 @@ class GenericDropHandlerBase : DropHandler {
|
|||
}
|
||||
|
||||
void drop(scope DropPackage* dropPackage) {
|
||||
if(!acceptedFormat || acceptedHandler is null)
|
||||
if(!acceptedFormat || acceptedHandler is null) {
|
||||
debug(sdpy_dnd) { import std.stdio; writeln("drop called w/ handler ", acceptedHandler, " and format ", acceptedFormat); }
|
||||
return; // prolly shouldn't happen anyway...
|
||||
}
|
||||
|
||||
dropPackage.getData(acceptedAction, acceptedFormat, acceptedHandler);
|
||||
}
|
||||
|
|
27
terminal.d
27
terminal.d
|
@ -174,6 +174,20 @@ import core.stdc.stdio;
|
|||
|
||||
version(TerminalDirectToEmulator) {
|
||||
version=WithEncapsulatedSignals;
|
||||
private __gshared bool windowGone = false;
|
||||
private bool forceTerminationTried = false;
|
||||
private void forceTermination() {
|
||||
if(forceTerminationTried) {
|
||||
// why are we still here?! someone must be catching the exception and calling back.
|
||||
// there's no recovery so time to kill this program.
|
||||
import core.stdc.stdlib;
|
||||
abort();
|
||||
} else {
|
||||
// give them a chance to cleanly exit...
|
||||
forceTerminationTried = true;
|
||||
throw new HangupException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
version(Posix) {
|
||||
|
@ -2705,6 +2719,7 @@ struct RealTimeConsoleInput {
|
|||
import core.time;
|
||||
if(terminal.tew.terminalEmulator.pendingForApplication.length)
|
||||
return true;
|
||||
if(windowGone) forceTermination();
|
||||
if(terminal.tew.terminalEmulator.outgoingSignal.wait(milliseconds.msecs))
|
||||
// it was notified, but it could be left over from stuff we
|
||||
// already processed... so gonna check the blocking conditions here too
|
||||
|
@ -2795,8 +2810,10 @@ struct RealTimeConsoleInput {
|
|||
moar:
|
||||
//if(interruptable && inputQueue.length)
|
||||
//return -1;
|
||||
if(terminal.tew.terminalEmulator.pendingForApplication.length == 0)
|
||||
if(terminal.tew.terminalEmulator.pendingForApplication.length == 0) {
|
||||
if(windowGone) forceTermination();
|
||||
terminal.tew.terminalEmulator.outgoingSignal.wait();
|
||||
}
|
||||
synchronized(terminal.tew.terminalEmulator) {
|
||||
if(terminal.tew.terminalEmulator.pendingForApplication.length == 0) {
|
||||
if(interruptable)
|
||||
|
@ -5626,6 +5643,7 @@ class LineGetter {
|
|||
terminal.tew.terminalEmulator.waitingForInboundSync = true;
|
||||
terminal.writeStringRaw("\xff");
|
||||
terminal.flush();
|
||||
if(windowGone) forceTermination();
|
||||
terminal.tew.terminalEmulator.syncSignal.wait();
|
||||
}
|
||||
|
||||
|
@ -7822,8 +7840,10 @@ version(TerminalDirectToEmulator) {
|
|||
terminalEmulator = new TerminalEmulatorInsideWidget(this);
|
||||
super(parent);
|
||||
this.parentWindow.win.onClosing = {
|
||||
if(term)
|
||||
if(term) {
|
||||
term.hangedUp = true;
|
||||
// should I just send an official SIGHUP?!
|
||||
}
|
||||
|
||||
if(auto wi = cast(TerminalEmulatorWindow) this.parentWindow) {
|
||||
if(wi.parent)
|
||||
|
@ -7860,6 +7880,8 @@ version(TerminalDirectToEmulator) {
|
|||
terminalEmulator.outgoingSignal.notify();
|
||||
terminalEmulator.incomingSignal.notify();
|
||||
terminalEmulator.syncSignal.notify();
|
||||
|
||||
windowGone = true;
|
||||
};
|
||||
|
||||
this.parentWindow.win.addEventListener((InputEventInternal ie) {
|
||||
|
@ -7875,6 +7897,7 @@ version(TerminalDirectToEmulator) {
|
|||
void sendRawInput(const(ubyte)[] data) {
|
||||
if(this.parentWindow) {
|
||||
this.parentWindow.win.postEvent(new InputEventInternal(data));
|
||||
if(windowGone) forceTermination();
|
||||
terminalEmulator.incomingSignal.wait(); // blocking write basically, wait until the TE confirms the receipt of it
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1311,7 +1311,11 @@ class TerminalEmulator {
|
|||
(esc[0] == '[' && (b >= 64 && b <= 126)) ||
|
||||
(esc[0] == ']' && b == '\007')))
|
||||
{
|
||||
tryEsc(esc[]);
|
||||
try {
|
||||
tryEsc(esc[]);
|
||||
} catch(Exception e) {
|
||||
unknownEscapeSequence(e.msg ~ " :: " ~ cast(char[]) esc[]);
|
||||
}
|
||||
esc = null;
|
||||
readingEsc = false;
|
||||
} else if(esc.length == 3 && esc[0] == '%' && esc[1] == 'G') {
|
||||
|
@ -1625,16 +1629,44 @@ class TerminalEmulator {
|
|||
notifyScrollbarPosition(currentScrollbackX, currentScrollback ? scrollbackLength - currentScrollback : int.max);
|
||||
}
|
||||
|
||||
/++
|
||||
Writes the text in the scrollback buffer to the given file.
|
||||
|
||||
Discards formatting information and embedded images.
|
||||
|
||||
See_Also:
|
||||
[writeScrollbackToDelegate]
|
||||
+/
|
||||
public void writeScrollbackToFile(string filename) {
|
||||
import std.stdio;
|
||||
auto file = File(filename, "wt");
|
||||
foreach(line; scrollbackBuffer[]) {
|
||||
foreach(c; line)
|
||||
file.write(c.ch); // I hope this is buffered
|
||||
if(!c.hasNonCharacterData)
|
||||
file.write(c.ch); // I hope this is buffered
|
||||
file.writeln();
|
||||
}
|
||||
}
|
||||
|
||||
/++
|
||||
Writes the text in the scrollback buffer to the given delegate, one character at a time.
|
||||
|
||||
Discards formatting information and embedded images.
|
||||
|
||||
See_Also:
|
||||
[writeScrollbackToFile]
|
||||
History:
|
||||
Added March 14, 2021 (dub version 9.4)
|
||||
+/
|
||||
public void writeScrollbackToDelegate(scope void delegate(dchar c) dg) {
|
||||
foreach(line; scrollbackBuffer[]) {
|
||||
foreach(c; line)
|
||||
if(!c.hasNonCharacterData)
|
||||
dg(c.ch);
|
||||
dg('\n');
|
||||
}
|
||||
}
|
||||
|
||||
public void drawScrollback(bool useAltScreen = false) {
|
||||
showScrollbackOnScreen(useAltScreen ? alternateScreen : normalScreen, 0, true, 0);
|
||||
}
|
||||
|
@ -1836,6 +1868,13 @@ class TerminalEmulator {
|
|||
bool mouseButtonReleaseTracking;
|
||||
bool mouseButtonMotionTracking;
|
||||
bool selectiveMouseTracking;
|
||||
/+
|
||||
When set, it causes xterm to send CSI I when the terminal gains focus, and CSI O when it loses focus.
|
||||
this is turned on by mode 1004 with mouse events.
|
||||
|
||||
FIXME: not implemented.
|
||||
+/
|
||||
bool sendFocusEvents;
|
||||
|
||||
bool mouseMotionTracking() {
|
||||
return _mouseMotionTracking;
|
||||
|
@ -1851,6 +1890,7 @@ class TerminalEmulator {
|
|||
mouseButtonTracking = false;
|
||||
mouseButtonReleaseTracking = false;
|
||||
mouseButtonMotionTracking = false;
|
||||
sendFocusEvents = false;
|
||||
}
|
||||
|
||||
bool wraparoundMode = true;
|
||||
|
@ -2337,7 +2377,13 @@ class TerminalEmulator {
|
|||
return bfr[0 .. max(argsAtSidx[sidx - 1].length, defaults.length)];
|
||||
}
|
||||
|
||||
auto argsSection = cast(char[]) esc[sidx .. $-1];
|
||||
auto end = esc.length - 1;
|
||||
foreach(iii, b; esc[sidx .. end]) {
|
||||
if(b >= 0x20 && b < 0x30)
|
||||
end = iii + sidx;
|
||||
}
|
||||
|
||||
auto argsSection = cast(char[]) esc[sidx .. end];
|
||||
int[] args = argsAtSidxBuffer[sidx - 1][];
|
||||
|
||||
import std.string : split;
|
||||
|
@ -2995,6 +3041,9 @@ P s = 2 3 ; 2 → Restore xterm window title from stack.
|
|||
mouseButtonReleaseTracking = true;
|
||||
mouseMotionTracking = true;
|
||||
break;
|
||||
case 1004:
|
||||
sendFocusEvents = true;
|
||||
break;
|
||||
case 1005:
|
||||
// enable utf-8 mouse mode
|
||||
/*
|
||||
|
@ -3102,6 +3151,9 @@ URXVT (1015)
|
|||
case 34:
|
||||
// no idea. vim inside screen sends it
|
||||
break;
|
||||
case 1004:
|
||||
sendFocusEvents = false;
|
||||
break;
|
||||
case 1005:
|
||||
// turn off utf-8 mouse
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue