ketmar patches

This commit is contained in:
Adam D. Ruppe 2017-03-14 22:13:42 -04:00
parent fd1e3b0585
commit 35d7806ad3
4 changed files with 202 additions and 18 deletions

109
image.d
View File

@ -1,6 +1,113 @@
/// this file just imports all available image decoders
/// this file imports all available image decoders, and provides convenient functions to load image regardless of it's format.
module arsd.image;
public import arsd.color;
public import arsd.png;
public import arsd.jpeg;
public import arsd.bmp;
static if (__traits(compiles, { import iv.vfs; })) enum ArsdImageHasIVVFS = true; else enum ArsdImageHasIVVFS = false;
private bool strEquCI (const(char)[] s0, const(char)[] s1) pure nothrow @trusted @nogc {
if (s0.length != s1.length) return false;
foreach (immutable idx, char ch; s0) {
if (ch >= 'A' && ch <= 'Z') ch += 32; // poor man's tolower()
char c1 = s1.ptr[idx];
if (c1 >= 'A' && c1 <= 'Z') c1 += 32; // poor man's tolower()
if (ch != c1) return false;
}
return true;
}
/// Image formats `arsd.image` can load (except `Unknown`, of course).
enum ImageFormat {
Unknown, ///
Png, ///
Bmp, ///
Jpeg, ///
}
/// Try to guess image format from file extension.
public ImageFormat guessImageFormatFromExtension (const(char)[] filename) {
if (filename.length < 2) return ImageFormat.Unknown;
size_t extpos = filename.length;
version(Windows) {
while (extpos > 0 && filename.ptr[extpos-1] != '.' && filename.ptr[extpos-1] != '/' && filename.ptr[extpos-1] != '\\' && filename.ptr[extpos-1] != ':') --extpos;
} else {
while (extpos > 0 && filename.ptr[extpos-1] != '.' && filename.ptr[extpos-1] != '/') --extpos;
}
if (extpos == 0 || filename.ptr[extpos-1] != '.') throw new Exception("cannot determine file format from extension");
auto ext = filename[extpos..$];
if (strEquCI(ext, "png")) return ImageFormat.Png;
if (strEquCI(ext, "bmp")) return ImageFormat.Bmp;
if (strEquCI(ext, "jpg") || strEquCI(ext, "jpeg")) return ImageFormat.Jpeg;
return ImageFormat.Unknown;
}
/// Try to guess image format by first data bytes.
public ImageFormat guessImageFormatFromMemory (const(void)[] membuf) {
auto buf = cast(const(ubyte)[])membuf;
if (buf.length == 0) return ImageFormat.Unknown;
// detect file format
// png
if (buf.length > 7 && buf.ptr[0] == 0x89 && buf.ptr[1] == 0x50 && buf.ptr[2] == 0x4E &&
buf.ptr[3] == 0x47 && buf.ptr[4] == 0x0D && buf.ptr[5] == 0x0A && buf.ptr[6] == 0x1A)
{
return ImageFormat.Png;
}
// bmp
if (buf.length > 6 && buf.ptr[0] == 'B' && buf.ptr[1] == 'M') {
uint datasize = buf.ptr[2]|(buf.ptr[3]<<8)|(buf.ptr[4]<<16)|(buf.ptr[5]<<24);
if (datasize > 6 && datasize <= buf.length) return ImageFormat.Bmp;
}
// jpg
try {
int width, height, components;
if (detect_jpeg_image_from_memory(buf, width, height, components)) return ImageFormat.Jpeg;
} catch (Exception e) {} // sorry
return ImageFormat.Unknown;
}
/// Try to guess image format from file name and load that image.
public MemoryImage loadImageFromFile(T:const(char)[]) (T filename) {
static if (is(T == typeof(null))) {
throw new Exception("cannot load image from unnamed file");
} else {
final switch (guessImageFormatFromExtension(filename)) {
case ImageFormat.Unknown: throw new Exception("cannot determine file format from extension");
case ImageFormat.Png: static if (is(T == string)) return readPng(filename); else return readPng(filename.idup);
case ImageFormat.Bmp: static if (is(T == string)) return readBmp(filename); else return readBmp(filename.idup);
case ImageFormat.Jpeg: return readJpeg(filename);
}
}
}
/// Try to guess image format from data and load that image.
public MemoryImage loadImageFromMemory (const(void)[] membuf) {
final switch (guessImageFormatFromMemory(membuf)) {
case ImageFormat.Unknown: throw new Exception("cannot determine file format");
case ImageFormat.Png: return imageFromPng(readPng(cast(const(ubyte)[])membuf));
case ImageFormat.Bmp: return readBmp(cast(const(ubyte)[])membuf);
case ImageFormat.Jpeg: return readJpegFromMemory(cast(const(ubyte)[])membuf);
}
}
static if (ArsdImageHasIVVFS) {
import iv.vfs;
public MemoryImage loadImageFromFile (VFile fl) {
auto fsz = fl.size-fl.tell;
if (fsz < 4) throw new Exception("cannot determine file format");
if (fsz > int.max/8) throw new Exception("image data too big");
auto data = new ubyte[](cast(uint)fsz);
scope(exit) delete data; // this should be safe, as image will copy data to it's internal storage
fl.rawReadExact(data);
return loadImageFromMemory(data);
}
}

View File

@ -64,6 +64,8 @@ module arsd.minigui;
* rich text
*/
alias HWND=void*;
abstract class ComboboxBase : Widget {
// if the user can enter arbitrary data, we want to use 2 == CBS_DROPDOWN
// or to always show the list, we want CBS_SIMPLE == 1
@ -2581,7 +2583,7 @@ struct TBBUTTON {
int idCommand;
BYTE fsState;
BYTE fsStyle;
BYTE bReserved[2]; // FIXME: isn't that different on 64 bit?
BYTE[2] bReserved; // FIXME: isn't that different on 64 bit?
DWORD dwData;
int iString;
}

View File

@ -799,7 +799,8 @@ class SimpleWindow : CapableOfHandlingNativeEvent {
this.resizability = resizable;
this.windowType = windowType;
this.customizationFlags = customizationFlags;
impl.createWindow(width, height, title is null ? "D Application" : title, opengl, parent);
this._title = (title is null ? "D Application" : title);
impl.createWindow(width, height, this._title, opengl, parent);
}
/// Same as above, except using the `Size` struct instead of separate width and height.
@ -909,8 +910,11 @@ class SimpleWindow : CapableOfHandlingNativeEvent {
/// Closes the window. If there are no more open windows, the event loop will terminate.
void close() {
impl.closeWindow();
_closed = true;
if (!_closed) {
if (onClosing !is null) onClosing();
impl.closeWindow();
_closed = true;
}
}
/// Alias for `hidden = false`
@ -1329,9 +1333,18 @@ class SimpleWindow : CapableOfHandlingNativeEvent {
/// Handles a timer pulse. Settable through setEventHandlers.
void delegate() handlePulse;
/// called when the focus changes, param is if we have it (true) or are losing it (false)
/// Called when the focus changes, param is if we have it (true) or are losing it (false).
void delegate(bool) onFocusChange;
/** Called inside `close()` method. Our window is still alive, and we can free various resources.
* Sometimes it is easier to setup the delegate instead of subclassing. */
void delegate() onClosing;
/** Called when we received destroy notification. At this stage we cannot do much with our window
* (as it is already dead, and it's native handle cannot be used), but we still can do some
* last minute cleanup. */
void delegate() onDestroyed;
private {
int lastMouseX = int.min;
int lastMouseY = int.min;
@ -4576,6 +4589,7 @@ version(Windows) {
DestroyWindow(hwnd);
break;
case WM_DESTROY:
if (this.onDestroyed !is null) try { this.onDestroyed(); } catch (Exception e) {} // sorry
SimpleWindow.nativeMapping.remove(hwnd);
CapableOfHandlingNativeEvent.nativeHandleMapping.remove(hwnd);
if(SimpleWindow.nativeMapping.keys.length == 0)
@ -6362,6 +6376,7 @@ version(X11) {
break;
case EventType.DestroyNotify:
if(auto win = e.xdestroywindow.window in SimpleWindow.nativeMapping) {
if (win.onDestroyed !is null) try { win.onDestroyed(); } catch (Exception e) {} // sorry
win._closed = true; // just in case
win.destroyed = true;
if (win.xic !is null) {

View File

@ -1,5 +1,5 @@
/++
Module for interacting with the user's terminal, including color output, cursor manipulation, and full-featured real-time mouse and keyboard input.
Module for interacting with the user's terminal, including color output, cursor manipulation, and full-featured real-time mouse and keyboard input. Also includes high-level convenience methods, like [Terminal.getline], which gives the user a line editor with history, completion, etc. See the [#examples].
The main interface for this module is the Terminal struct, which
@ -19,7 +19,7 @@
On Mac Terminal btw, a lot of hacks are needed and mouse support doesn't work. Most functions basically
work now though.
ROADMAP:
Future_Roadmap:
$(LIST
* The CharacterEvent and NonCharacterKeyEvent types will be removed. Instead, use KeyboardEvent
on new programs.
@ -63,6 +63,36 @@
+/
module arsd.terminal;
/++
This example will demonstrate the high-level getline interface.
The user will be able to type a line and navigate around it with cursor keys and even the mouse on some systems, as well as perform editing as they expect (e.g. the backspace and delete keys work normally) until they press enter. Then, the final line will be returned to your program, which the example will simply print back to the user.
+/
unittest {
import arsd.terminal;
void main() {
auto terminal = Terminal(ConsoleOutputType.linear);
string line = terminal.getline();
terminal.writeln("You wrote: ", line);
}
}
/++
This example demonstrates color output, using [Terminal.color]
and the output functions like [Terminal.writeln].
+/
unittest {
import arsd.terminal;
void main() {
auto terminal = Terminal(ConsoleOutputType.linear);
terminal.color(Color.green, Color.black);
terminal.writeln("Hello world, in green on black!");
terminal.color(Color.DEFAULT, Color.DEFAULT);
terminal.writeln("And back to normal.");
}
}
/*
Widgets:
tab widget
@ -2291,10 +2321,11 @@ struct RealTimeConsoleInput {
/// The new style of keyboard event
struct KeyboardEvent {
bool pressed;
dchar which;
uint modifierState;
bool pressed; ///
dchar which; ///
uint modifierState; ///
///
bool isCharacter() {
return !(which >= Key.min && which <= Key.max);
}
@ -2332,6 +2363,7 @@ struct KeyboardEvent {
}
/// Deprecated: use KeyboardEvent instead in new programs
/// Input event for characters
struct CharacterEvent {
/// .
@ -2345,6 +2377,7 @@ struct CharacterEvent {
uint modifierState; /// Don't depend on this to be available for character events
}
/// Deprecated: use KeyboardEvent instead in new programs
struct NonCharacterKeyEvent {
/// .
enum Type {
@ -2421,7 +2454,7 @@ struct MouseEvent {
uint modifierState; /// shift, ctrl, alt, meta, altgr. Not always available. Always check by using modifierState & ModifierState.something
}
/// .
/// When you get this, check terminal.width and terminal.height to see the new size and react accordingly.
struct SizeChangedEvent {
int oldWidth;
int oldHeight;
@ -2467,13 +2500,27 @@ enum ModifierState : uint {
windows = 512 // only available if you are using my terminal emulator; it isn't actually offered on standard linux ones
}
/// GetNextEvent returns this. Check the type, then use get to get the more detailed input
version(DDoc)
///
enum ModifierState : uint {
///
shift = 4,
///
alt = 2,
///
control = 16,
}
/++
[RealTimeConsoleInput.nextEvent] returns one of these. Check the type, then use the [InputEvent.get|get] method to get the more detailed information about the event.
++/
struct InputEvent {
/// .
enum Type {
KeyboardEvent, ///.
CharacterEvent, ///.
NonCharacterKeyEvent, /// .
KeyboardEvent, /// Keyboard key pressed (or released, where supported)
CharacterEvent, /// Do not use this in new programs, use KeyboardEvent instead
NonCharacterKeyEvent, /// Do not use this in new programs, use KeyboardEvent instead
PasteEvent, /// The user pasted some text. Not always available, the pasted text might come as a series of character events instead.
MouseEvent, /// only sent if you subscribed to mouse events
SizeChangedEvent, /// only sent if you subscribed to size events
@ -2492,7 +2539,19 @@ struct InputEvent {
/// It may be null in the case of program-generated events;
@property Terminal* terminal() { return term; }
/// .
/++
Gets the specific event instance. First, check the type (such as in a `switch` statement, then extract the correct one from here. Note that the template argument is a $(B value type of the enum above), not a type argument. So to use it, do $(D event.get!(InputEvent.Type.KeyboardEvent)), for example.
See_Also:
The event types:
[KeyboardEvent], [MouseEvent], [SizeChangedEvent],
[PasteEvent], [UserInterruptionEvent],
[EndOfFileEvent], [HangupEvent], [CustomEvent]
And associated functions:
[RealTimeConsoleInput], [ConsoleInputFlags]
++/
@property auto get(Type T)() {
if(type != T)
throw new Exception("Wrong event type");
@ -2519,7 +2578,7 @@ struct InputEvent {
else static assert(0, "Type " ~ T.stringof ~ " not added to the get function");
}
// custom event is public because otherwise there's no point at all
/// custom event is public because otherwise there's no point at all
this(CustomEvent c, Terminal* p = null) {
t = Type.CustomEvent;
customEvent = c;
@ -2582,6 +2641,7 @@ struct InputEvent {
}
version(Demo)
/// View the source of this!
void main() {
auto terminal = Terminal(ConsoleOutputType.cellular);