mirror of https://github.com/adamdruppe/arsd.git
update
This commit is contained in:
parent
d5c3539293
commit
3d396dfaa6
7
cgi.d
7
cgi.d
|
@ -2114,7 +2114,7 @@ class Cgi {
|
|||
None
|
||||
}
|
||||
|
||||
/+
|
||||
/++
|
||||
Sets an HTTP cookie, automatically encoding the data to the correct string.
|
||||
expiresIn is how many milliseconds in the future the cookie will expire.
|
||||
TIP: to make a cookie accessible from subdomains, set the domain to .yourdomain.com.
|
||||
|
@ -3354,15 +3354,10 @@ 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;
|
||||
|
|
8
http2.d
8
http2.d
|
@ -343,7 +343,8 @@ struct HttpResponse {
|
|||
// ignore
|
||||
}
|
||||
|
||||
header = header[1 .. $];
|
||||
if(header.length)
|
||||
header = header[1 .. $];
|
||||
}
|
||||
|
||||
ret ~= current;
|
||||
|
@ -1529,8 +1530,11 @@ class HttpRequest {
|
|||
|
||||
bodyReadingState.chunkedState = 0;
|
||||
|
||||
while(data[a] != 10)
|
||||
while(data[a] != 10) {
|
||||
a++;
|
||||
if(a == data.length)
|
||||
return stillAlive; // in the footer state we're just discarding everything until we're done so this should be ok
|
||||
}
|
||||
data = data[a + 1 .. $];
|
||||
|
||||
if(bodyReadingState.isGzipped || bodyReadingState.isDeflated) {
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
|
||||
|
||||
// https://www.x.org/releases/X11R7.7/doc/libXext/dbelib.html
|
||||
// https://www.x.org/releases/X11R7.6/doc/libXext/synclib.html
|
||||
|
||||
|
||||
// on Mac with X11: -L-L/usr/X11/lib
|
||||
|
||||
|
|
73
terminal.d
73
terminal.d
|
@ -1628,7 +1628,17 @@ struct Terminal {
|
|||
|
||||
Params:
|
||||
text = text displayed in the terminal
|
||||
identifier = an additional number attached to the text and returned to you in a [LinkEvent]
|
||||
|
||||
identifier = an additional number attached to the text and returned to you in a [LinkEvent].
|
||||
Possible uses of this are to have a small number of "link classes" that are handled based on
|
||||
the text. For example, maybe identifier == 0 means paste text into the line. identifier == 1
|
||||
could mean open a browser. identifier == 2 might open details for it. Just be sure to encode
|
||||
the bulk of the information into the text so the user can copy/paste it out too.
|
||||
|
||||
You may also create a mapping of (identifier,text) back to some other activity, but if you do
|
||||
that, be sure to check [hyperlinkSupported] and fallback in your own code so it still makes
|
||||
sense to users on other terminals.
|
||||
|
||||
autoStyle = set to `false` to suppress the automatic color and underlining of the text.
|
||||
|
||||
Bugs:
|
||||
|
@ -1662,6 +1672,21 @@ struct Terminal {
|
|||
}
|
||||
}
|
||||
|
||||
/++
|
||||
Returns true if the terminal advertised compatibility with the [hyperlink] function's
|
||||
implementation.
|
||||
|
||||
History:
|
||||
Added April 2, 2021
|
||||
+/
|
||||
bool hyperlinkSupported() {
|
||||
if((tcaps & TerminalCapabilities.arsdHyperlinks)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: the Windows console does not support underlining
|
||||
void underline(bool set, ForceOption force = ForceOption.automatic) {
|
||||
if(set == _underlined && force != ForceOption.alwaysSend)
|
||||
|
@ -3871,9 +3896,9 @@ struct PasteEvent {
|
|||
Added March 18, 2020
|
||||
+/
|
||||
struct LinkEvent {
|
||||
string text; ///
|
||||
ushort identifier; ///
|
||||
ushort command; /// set by the terminal to indicate how it was clicked. values tbd
|
||||
string text; /// the text visible to the user that they clicked on
|
||||
ushort identifier; /// the identifier set when you output the link. This is small because it is packed into extra bits on the text, one bit per character.
|
||||
ushort command; /// set by the terminal to indicate how it was clicked. values tbd, currently always 0
|
||||
}
|
||||
|
||||
/// .
|
||||
|
@ -6347,6 +6372,10 @@ class LineGetter {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case InputEvent.Type.LinkEvent:
|
||||
if(handleLinkEvent !is null)
|
||||
handleLinkEvent(e.linkEvent, this);
|
||||
break;
|
||||
case InputEvent.Type.SizeChangedEvent:
|
||||
/* We'll adjust the bounding box. If you don't like this, handle SizeChangedEvent
|
||||
yourself and then don't pass it to this function. */
|
||||
|
@ -6368,6 +6397,29 @@ class LineGetter {
|
|||
return true;
|
||||
}
|
||||
|
||||
/++
|
||||
Gives a convenience hook for subclasses to handle my terminal's hyperlink extension.
|
||||
|
||||
|
||||
You can also handle these by filtering events before you pass them to [workOnLine].
|
||||
That's still how I recommend handling any overrides or custom events, but making this
|
||||
a delegate is an easy way to inject handlers into an otherwise linear i/o application.
|
||||
|
||||
Does nothing if null.
|
||||
|
||||
It passes the event as well as the current line getter to the delegate. You may simply
|
||||
`lg.addString(ev.text); lg.redraw();` in some cases.
|
||||
|
||||
History:
|
||||
Added April 2, 2021.
|
||||
|
||||
See_Also:
|
||||
[Terminal.hyperlink]
|
||||
|
||||
[TerminalCapabilities.arsdHyperlinks]
|
||||
+/
|
||||
void delegate(LinkEvent ev, LineGetter lg) handleLinkEvent;
|
||||
|
||||
/++
|
||||
Replaces the line currently being edited with the given line and positions the cursor inside it.
|
||||
|
||||
|
@ -7527,6 +7579,18 @@ version(TerminalDirectToEmulator) {
|
|||
Represents the window that the library pops up for you.
|
||||
+/
|
||||
final class TerminalEmulatorWindow : MainWindow {
|
||||
/++
|
||||
Returns the size of an individual character cell, in pixels.
|
||||
|
||||
History:
|
||||
Added April 2, 2021
|
||||
+/
|
||||
Size characterCellSize() {
|
||||
if(tew && tew.terminalEmulator)
|
||||
return Size(tew.terminalEmulator.fontWidth, tew.terminalEmulator.fontHeight);
|
||||
else
|
||||
return Size(1, 1);
|
||||
}
|
||||
|
||||
/++
|
||||
Gives access to the underlying terminal emulation object.
|
||||
|
@ -7943,6 +8007,7 @@ version(TerminalDirectToEmulator) {
|
|||
widget.smw.setViewableArea(this.width, this.height);
|
||||
widget.smw.setPageSize(this.width / 2, this.height / 2);
|
||||
}
|
||||
notifyScrollbarPosition(0, int.max);
|
||||
clearScreenRequested = true;
|
||||
if(widget && widget.term)
|
||||
widget.term.windowSizeChanged = true;
|
||||
|
|
|
@ -45,12 +45,15 @@ interface NonCharacterData {
|
|||
//const(ubyte)[] serialize();
|
||||
}
|
||||
|
||||
struct BrokenUpImage {
|
||||
struct BinaryDataTerminalRepresentation {
|
||||
int width;
|
||||
int height;
|
||||
TerminalEmulator.TerminalCell[] representation;
|
||||
}
|
||||
|
||||
// old name, don't use in new programs anymore.
|
||||
deprecated alias BrokenUpImage = BinaryDataTerminalRepresentation;
|
||||
|
||||
struct CustomGlyph {
|
||||
TrueColorImage image;
|
||||
dchar substitute;
|
||||
|
@ -864,8 +867,8 @@ class TerminalEmulator {
|
|||
}
|
||||
|
||||
/// if a binary extension is triggered, the implementing class is responsible for figuring out how it should be made to fit into the screen buffer
|
||||
protected /*abstract*/ BrokenUpImage handleBinaryExtensionData(const(ubyte)[]) {
|
||||
return BrokenUpImage();
|
||||
protected /*abstract*/ BinaryDataTerminalRepresentation handleBinaryExtensionData(const(ubyte)[]) {
|
||||
return BinaryDataTerminalRepresentation();
|
||||
}
|
||||
|
||||
/// If you subclass this and return true, you can scroll on command without needing to redraw the entire screen;
|
||||
|
@ -1504,16 +1507,24 @@ class TerminalEmulator {
|
|||
|
||||
int max = cast(int) scrollbackBuffer.length - screenHeight;
|
||||
if(scrollbackReflow && max < 0) {
|
||||
foreach(line; scrollbackBuffer[])
|
||||
max += cast(int) line.length / screenWidth;
|
||||
foreach(line; scrollbackBuffer[]) {
|
||||
if(line.length > 2 && (line[0].hasNonCharacterData || line[$-1].hasNonCharacterData))
|
||||
max += 0;
|
||||
else
|
||||
max += cast(int) line.length / screenWidth;
|
||||
}
|
||||
}
|
||||
|
||||
if(max < 0)
|
||||
max = 0;
|
||||
|
||||
if(scrollbackReflow && currentScrollback > max) {
|
||||
foreach(line; scrollbackBuffer[])
|
||||
max += cast(int) line.length / screenWidth;
|
||||
foreach(line; scrollbackBuffer[]) {
|
||||
if(line.length > 2 && (line[0].hasNonCharacterData || line[$-1].hasNonCharacterData))
|
||||
max += 0;
|
||||
else
|
||||
max += cast(int) line.length / screenWidth;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentScrollback > max)
|
||||
|
@ -1615,7 +1626,10 @@ class TerminalEmulator {
|
|||
int max;
|
||||
if(scrollbackReflow) {
|
||||
foreach(line; scrollbackBuffer[]) {
|
||||
count += cast(int) line.length / screenWidth;
|
||||
if(line.length > 2 && (line[0].hasNonCharacterData || line[$-1].hasNonCharacterData))
|
||||
{} // intentionally blank, the count is fine since this line isn't reflowed anyway
|
||||
else
|
||||
count += cast(int) line.length / screenWidth;
|
||||
}
|
||||
} else {
|
||||
foreach(line; scrollbackBuffer[]) {
|
||||
|
@ -1684,6 +1698,11 @@ class TerminalEmulator {
|
|||
int idx = cast(int) scrollbackBuffer.length - 1;
|
||||
foreach_reverse(line; scrollbackBuffer[]) {
|
||||
auto lineCount = 1 + line.length / screenWidth;
|
||||
|
||||
// if the line has an image in it, it cannot be reflowed. this hack to check just the first and last thing is the cheapest way rn
|
||||
if(line.length > 2 && (line[0].hasNonCharacterData || line[$-1].hasNonCharacterData))
|
||||
lineCount = 1;
|
||||
|
||||
numLines += lineCount;
|
||||
if(numLines >= (screenHeight + howFar)) {
|
||||
start = cast(int) idx;
|
||||
|
@ -1736,6 +1755,9 @@ class TerminalEmulator {
|
|||
|
||||
if(cursorX == screenWidth-1) {
|
||||
if(scrollbackReflow) {
|
||||
// don't attempt to reflow images
|
||||
if(cell.hasNonCharacterData)
|
||||
break;
|
||||
cursorX = 0;
|
||||
if(cursorY + 1 == screenHeight)
|
||||
break outer;
|
||||
|
@ -1765,7 +1787,8 @@ class TerminalEmulator {
|
|||
if(scrollLock)
|
||||
toggleScrollLock();
|
||||
|
||||
endScrollback(); // FIXME: hack
|
||||
// FIXME: hack
|
||||
endScrollback();
|
||||
|
||||
screenWidth = w;
|
||||
screenHeight = h;
|
||||
|
@ -2177,7 +2200,11 @@ class TerminalEmulator {
|
|||
} else {
|
||||
if(!scrollbackReflow && line.length > scrollbackWidth_)
|
||||
scrollbackWidth_ = cast(int) line.length;
|
||||
scrollbackLength = cast(int) (scrollbackLength + 1 + (scrollbackBuffer[cast(int) scrollbackBuffer.length - 1].length) / screenWidth);
|
||||
|
||||
if(line.length > 2 && (line[0].hasNonCharacterData || line[$-1].hasNonCharacterData))
|
||||
scrollbackLength = scrollbackLength + 1;
|
||||
else
|
||||
scrollbackLength = cast(int) (scrollbackLength + 1 + (scrollbackBuffer[cast(int) scrollbackBuffer.length - 1].length) / screenWidth);
|
||||
notifyScrollbackAdded();
|
||||
}
|
||||
|
||||
|
@ -4180,7 +4207,159 @@ mixin template SdpyImageSupport() {
|
|||
}
|
||||
}
|
||||
|
||||
protected override BrokenUpImage handleBinaryExtensionData(const(ubyte)[] binaryData) {
|
||||
version(TerminalDirectToEmulator)
|
||||
class NonCharacterData_Widget : NonCharacterData {
|
||||
this(void* data, size_t idx, int width, int height) {
|
||||
this.window = cast(SimpleWindow) data;
|
||||
this.idx = idx;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
void position(int posx, int posy, int width, int height) {
|
||||
if(posx == this.posx && posy == this.posy && width == this.pixelWidth && height == this.pixelHeight)
|
||||
return;
|
||||
this.posx = posx;
|
||||
this.posy = posy;
|
||||
this.pixelWidth = width;
|
||||
this.pixelHeight = height;
|
||||
|
||||
window.moveResize(posx, posy, width, height);
|
||||
import std.stdio; writeln(posx, " ", posy, " ", width, " ", height);
|
||||
|
||||
auto painter = this.window.draw;
|
||||
painter.outlineColor = Color.red;
|
||||
painter.fillColor = Color.green;
|
||||
painter.drawRectangle(Point(0, 0), width, height);
|
||||
|
||||
|
||||
}
|
||||
|
||||
SimpleWindow window;
|
||||
size_t idx;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
int posx;
|
||||
int posy;
|
||||
int pixelWidth;
|
||||
int pixelHeight;
|
||||
}
|
||||
|
||||
private struct CachedImage {
|
||||
ulong hash;
|
||||
BinaryDataTerminalRepresentation bui;
|
||||
int timesSeen;
|
||||
import core.time;
|
||||
MonoTime lastUsed;
|
||||
}
|
||||
private CachedImage[] imageCache;
|
||||
private CachedImage* findInCache(ulong hash) {
|
||||
if(hash == 0)
|
||||
return null;
|
||||
|
||||
/*
|
||||
import std.stdio;
|
||||
writeln("***");
|
||||
foreach(cache; imageCache) {
|
||||
writeln(cache.hash, " ", cache.timesSeen, " ", cache.lastUsed);
|
||||
}
|
||||
*/
|
||||
|
||||
foreach(ref i; imageCache)
|
||||
if(i.hash == hash) {
|
||||
import core.time;
|
||||
i.lastUsed = MonoTime.currTime;
|
||||
i.timesSeen++;
|
||||
return &i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private BinaryDataTerminalRepresentation addImageCache(ulong hash, BinaryDataTerminalRepresentation bui) {
|
||||
import core.time;
|
||||
if(imageCache.length == 0)
|
||||
imageCache.length = 8;
|
||||
|
||||
auto now = MonoTime.currTime;
|
||||
|
||||
size_t oldestIndex;
|
||||
MonoTime oldestTime = now;
|
||||
|
||||
size_t leastUsedIndex;
|
||||
int leastUsedCount = int.max;
|
||||
foreach(idx, ref cached; imageCache) {
|
||||
if(cached.hash == 0) {
|
||||
cached.hash = hash;
|
||||
cached.bui = bui;
|
||||
cached.timesSeen = 1;
|
||||
cached.lastUsed = now;
|
||||
|
||||
return bui;
|
||||
} else {
|
||||
if(cached.timesSeen < leastUsedCount) {
|
||||
leastUsedCount = cached.timesSeen;
|
||||
leastUsedIndex = idx;
|
||||
}
|
||||
if(cached.lastUsed < oldestTime) {
|
||||
oldestTime = cached.lastUsed;
|
||||
oldestIndex = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// need to overwrite one of the cached items, I'll just use the oldest one here
|
||||
// but maybe that could be smarter later
|
||||
|
||||
imageCache[oldestIndex].hash = hash;
|
||||
imageCache[oldestIndex].bui = bui;
|
||||
imageCache[oldestIndex].timesSeen = 1;
|
||||
imageCache[oldestIndex].lastUsed = now;
|
||||
|
||||
return bui;
|
||||
}
|
||||
|
||||
// It has a cache of the 8 most recently used items right now so if there's a loop of 9 you get pwned
|
||||
// but still the cache does an ok job at helping things while balancing out the big memory consumption it
|
||||
// could do if just left to grow and grow. i hope.
|
||||
protected override BinaryDataTerminalRepresentation handleBinaryExtensionData(const(ubyte)[] binaryData) {
|
||||
|
||||
version(none) {
|
||||
//version(TerminalDirectToEmulator)
|
||||
//if(binaryData.length == size_t.sizeof + 10) {
|
||||
//if((cast(uint[]) binaryData[0 .. 4])[0] == 0xdeadbeef && (cast(uint[]) binaryData[$-4 .. $])[0] == 0xabcdef32) {
|
||||
//auto widthInCharacterCells = binaryData[4];
|
||||
//auto heightInCharacterCells = binaryData[5];
|
||||
//auto pointer = (cast(void*[]) binaryData[6 .. $-4])[0];
|
||||
|
||||
auto widthInCharacterCells = 30;
|
||||
auto heightInCharacterCells = 20;
|
||||
SimpleWindow pwin;
|
||||
foreach(k, v; SimpleWindow.nativeMapping) {
|
||||
if(v.type == WindowTypes.normal)
|
||||
pwin = v;
|
||||
}
|
||||
auto pointer = cast(void*) (new SimpleWindow(640, 480, null, OpenGlOptions.no, Resizability.automaticallyScaleIfPossible, WindowTypes.nestedChild, WindowFlags.normal, pwin));
|
||||
|
||||
BinaryDataTerminalRepresentation bi;
|
||||
bi.width = widthInCharacterCells;
|
||||
bi.height = heightInCharacterCells;
|
||||
bi.representation.length = bi.width * bi.height;
|
||||
|
||||
foreach(idx, ref cell; bi.representation) {
|
||||
cell.nonCharacterData = new NonCharacterData_Widget(pointer, idx, widthInCharacterCells, heightInCharacterCells);
|
||||
}
|
||||
|
||||
return bi;
|
||||
//}
|
||||
}
|
||||
|
||||
import std.digest.md;
|
||||
|
||||
ulong hash = * (cast(ulong*) md5Of(binaryData).ptr);
|
||||
|
||||
if(auto cached = findInCache(hash))
|
||||
return cached.bui;
|
||||
|
||||
TrueColorImage mi;
|
||||
|
||||
if(binaryData.length > 8 && binaryData[1] == 'P' && binaryData[2] == 'N' && binaryData[3] == 'G') {
|
||||
|
@ -4196,7 +4375,7 @@ mixin template SdpyImageSupport() {
|
|||
import arsd.svg;
|
||||
NSVG* image = nsvgParse(cast(const(char)[]) binaryData);
|
||||
if(image is null)
|
||||
return BrokenUpImage();
|
||||
return BinaryDataTerminalRepresentation();
|
||||
|
||||
int w = cast(int) image.width + 1;
|
||||
int h = cast(int) image.height + 1;
|
||||
|
@ -4205,10 +4384,10 @@ mixin template SdpyImageSupport() {
|
|||
rasterize(rast, image, 0, 0, 1, mi.imageData.bytes.ptr, w, h, w*4);
|
||||
image.kill();
|
||||
} else {
|
||||
return BrokenUpImage();
|
||||
return BinaryDataTerminalRepresentation();
|
||||
}
|
||||
|
||||
BrokenUpImage bi;
|
||||
BinaryDataTerminalRepresentation bi;
|
||||
bi.width = mi.width / fontWidth + ((mi.width%fontWidth) ? 1 : 0);
|
||||
bi.height = mi.height / fontHeight + ((mi.height%fontHeight) ? 1 : 0);
|
||||
|
||||
|
@ -4239,10 +4418,10 @@ mixin template SdpyImageSupport() {
|
|||
ix = 0;
|
||||
iy += fontHeight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return bi;
|
||||
return addImageCache(hash, bi);
|
||||
//return bi;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4466,8 +4645,7 @@ mixin template SdpyDraw() {
|
|||
}
|
||||
hasBufferedInfo = true;
|
||||
} catch(Exception e) {
|
||||
import std.stdio;
|
||||
writeln(cast(uint) cell.ch, " :: ", e.msg);
|
||||
// import std.stdio; writeln(cast(uint) cell.ch, " :: ", e.msg);
|
||||
}
|
||||
//}
|
||||
} else if(cell.nonCharacterData !is null) {
|
||||
|
@ -4479,6 +4657,19 @@ mixin template SdpyDraw() {
|
|||
painter.drawRectangle(Point(posx, posy), fontWidth, fontHeight);
|
||||
painter.drawImage(Point(posx, posy), ncdi.data, Point(ncdi.imageOffsetX, ncdi.imageOffsetY), fontWidth, fontHeight);
|
||||
}
|
||||
version(TerminalDirectToEmulator)
|
||||
if(auto wdi = cast(NonCharacterData_Widget) cell.nonCharacterData) {
|
||||
flushBuffer();
|
||||
if(wdi.idx == 0) {
|
||||
wdi.position(posx, posy, fontWidth * wdi.width, fontHeight * wdi.height);
|
||||
/*
|
||||
painter.outlineColor = defaultBackground;
|
||||
painter.fillColor = defaultBackground;
|
||||
painter.drawRectangle(Point(posx, posy), fontWidth, fontHeight);
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if(!cell.hasNonCharacterData)
|
||||
|
|
Loading…
Reference in New Issue