1424 lines
56 KiB
C
1424 lines
56 KiB
C
/* Kuroko bindings for Yutani */
|
|
#include <assert.h>
|
|
#include <toaru/yutani.h>
|
|
#include <toaru/decorations.h>
|
|
#include <toaru/menu.h>
|
|
#include <toaru/text.h>
|
|
#include <kuroko/kuroko.h>
|
|
#include <kuroko/vm.h>
|
|
#include <kuroko/value.h>
|
|
#include <kuroko/object.h>
|
|
|
|
static KrkInstance * module;
|
|
static KrkInstance * yctxInstance = NULL;
|
|
|
|
#define S(c) (krk_copyString(c,sizeof(c)-1))
|
|
|
|
static KrkClass * Message;
|
|
struct MessageClass {
|
|
KrkInstance inst;
|
|
yutani_msg_t * msg;
|
|
};
|
|
|
|
static KrkClass * Yutani;
|
|
struct YutaniClass {
|
|
KrkInstance inst;
|
|
yutani_t * yctx;
|
|
};
|
|
|
|
static KrkClass * GraphicsContext;
|
|
struct GraphicsContext {
|
|
KrkInstance inst;
|
|
gfx_context_t * ctx;
|
|
int doubleBuffered;
|
|
};
|
|
|
|
static KrkClass * YutaniWindow;
|
|
struct WindowClass {
|
|
KrkInstance inst;
|
|
gfx_context_t * ctx;
|
|
int doubleBuffered;
|
|
yutani_window_t * window;
|
|
};
|
|
|
|
static KrkClass * YutaniSprite;
|
|
struct YutaniSprite {
|
|
KrkInstance inst;
|
|
gfx_context_t * ctx;
|
|
int doubleBuffered;
|
|
sprite_t sprite;
|
|
};
|
|
|
|
static KrkClass * YutaniColor;
|
|
struct YutaniColor {
|
|
KrkInstance inst;
|
|
uint32_t color;
|
|
};
|
|
|
|
static KrkClass * YutaniFont;
|
|
struct YutaniFont {
|
|
KrkInstance inst;
|
|
struct TT_Font * fontData;
|
|
int fontSize;
|
|
uint32_t fontColor;
|
|
};
|
|
|
|
static KrkClass * MenuBarClass;
|
|
struct MenuBarClass {
|
|
KrkInstance inst;
|
|
struct menu_bar menuBar;
|
|
};
|
|
|
|
static KrkClass * MenuListClass;
|
|
struct MenuListClass {
|
|
KrkInstance inst;
|
|
struct MenuList * menuList;
|
|
};
|
|
|
|
static KrkClass * MenuEntryClass;
|
|
struct MenuEntryClass {
|
|
KrkInstance inst;
|
|
struct MenuEntry * menuEntry;
|
|
};
|
|
|
|
static KrkClass * MenuEntrySubmenuClass;
|
|
static KrkClass * MenuEntrySeparatorClass;
|
|
|
|
/**
|
|
* Convenience wrapper to make a class and attach it to the module, while
|
|
* handling stack push/pop to keep things from being prematurely GC'd.
|
|
*/
|
|
KrkClass * krk_createClass(KrkInstance * inModule, const char * name, KrkClass * base) {
|
|
if (!base) base = vm.baseClasses->objectClass;
|
|
KrkString * str_Name = krk_copyString(name, strlen(name));
|
|
krk_push(OBJECT_VAL(str_Name));
|
|
KrkClass * obj_Class = krk_newClass(str_Name, base);
|
|
krk_push(OBJECT_VAL(obj_Class));
|
|
krk_attachNamedObject(&inModule->fields, name, (KrkObj *)obj_Class);
|
|
krk_pop(); /* obj_Class */
|
|
krk_pop(); /* str_Name */
|
|
|
|
return obj_Class;
|
|
}
|
|
|
|
#define DO_FIELD(name, body) \
|
|
{ krk_push(OBJECT_VAL(S(name))); \
|
|
if (krk_valuesEqual(argv[1], krk_peek(0))) { \
|
|
krk_pop(); \
|
|
body \
|
|
} krk_pop(); }
|
|
|
|
#define TO_INT_(name) { return INTEGER_VAL((msg-> name)); }
|
|
#define TO_INT(name) { return INTEGER_VAL((me-> name)); }
|
|
#define WID() DO_FIELD("wid", TO_INT(wid))
|
|
#define STRUCT(type) type * me = (void*)msg->data
|
|
|
|
static void _message_sweep(KrkInstance * self) {
|
|
free(((struct MessageClass*)self)->msg);
|
|
}
|
|
|
|
static KrkValue _message_getattr(int argc, const KrkValue argv[], int hasKw) {
|
|
assert(argc == 2);
|
|
KrkInstance * self = AS_INSTANCE(argv[0]);
|
|
|
|
yutani_msg_t * msg = ((struct MessageClass*)self)->msg;
|
|
if (!msg) return NONE_VAL();
|
|
|
|
DO_FIELD("magic", TO_INT_(magic));
|
|
DO_FIELD("type", TO_INT_(type));
|
|
DO_FIELD("size", TO_INT_(size));
|
|
|
|
switch (msg->type) {
|
|
case YUTANI_MSG_WELCOME: {
|
|
STRUCT(struct yutani_msg_welcome);
|
|
DO_FIELD("display_width", TO_INT(display_width));
|
|
DO_FIELD("display_height", TO_INT(display_height));
|
|
} break;
|
|
case YUTANI_MSG_WINDOW_MOUSE_EVENT: {
|
|
STRUCT(struct yutani_msg_window_mouse_event);
|
|
WID();
|
|
DO_FIELD("new_x", TO_INT(new_x));
|
|
DO_FIELD("new_y", TO_INT(new_y));
|
|
DO_FIELD("old_x", TO_INT(old_x));
|
|
DO_FIELD("old_y", TO_INT(old_y));
|
|
DO_FIELD("buttons", TO_INT(buttons));
|
|
DO_FIELD("command", TO_INT(command));
|
|
DO_FIELD("modifiers", TO_INT(modifiers));
|
|
} break;
|
|
case YUTANI_MSG_WINDOW_FOCUS_CHANGE: {
|
|
STRUCT(struct yutani_msg_window_focus_change);
|
|
WID();
|
|
DO_FIELD("focused", TO_INT(focused));
|
|
} break;
|
|
case YUTANI_MSG_RESIZE_OFFER: {
|
|
STRUCT(struct yutani_msg_window_resize);
|
|
WID();
|
|
DO_FIELD("width", TO_INT(width));
|
|
DO_FIELD("height", TO_INT(height));
|
|
DO_FIELD("bufid", TO_INT(bufid));
|
|
} break;
|
|
case YUTANI_MSG_WINDOW_ADVERTISE: {
|
|
STRUCT(struct yutani_msg_window_advertise);
|
|
WID();
|
|
DO_FIELD("flags", TO_INT(flags));
|
|
DO_FIELD("size", TO_INT(size));
|
|
DO_FIELD("width", TO_INT(width));
|
|
DO_FIELD("height", TO_INT(height));
|
|
DO_FIELD("bufid", TO_INT(bufid));
|
|
DO_FIELD("name", { char * s = me->strings; size_t l = strlen(s); return OBJECT_VAL(krk_copyString(s,l)); });
|
|
DO_FIELD("icon", { char * s = me->strings + me->icon; size_t l = strlen(s); return OBJECT_VAL(krk_copyString(s,l)); });
|
|
} break;
|
|
case YUTANI_MSG_WINDOW_MOVE: {
|
|
STRUCT(struct yutani_msg_window_move);
|
|
WID();
|
|
DO_FIELD("x", TO_INT(x));
|
|
DO_FIELD("y", TO_INT(y));
|
|
} break;
|
|
case YUTANI_MSG_KEY_EVENT: {
|
|
STRUCT(struct yutani_msg_key_event);
|
|
WID();
|
|
|
|
DO_FIELD("keycode", TO_INT(event.keycode));
|
|
DO_FIELD("modifiers", TO_INT(event.modifiers));
|
|
DO_FIELD("action", TO_INT(event.action));
|
|
DO_FIELD("key", TO_INT(event.key));
|
|
|
|
DO_FIELD("kbd_state", TO_INT(state.kbd_state));
|
|
DO_FIELD("kbd_s_state", TO_INT(state.kbd_s_state));
|
|
DO_FIELD("k_ctrl", TO_INT(state.k_ctrl));
|
|
DO_FIELD("k_shift", TO_INT(state.k_shift));
|
|
DO_FIELD("k_alt", TO_INT(state.k_alt));
|
|
DO_FIELD("k_super", TO_INT(state.k_super));
|
|
DO_FIELD("kl_ctrl", TO_INT(state.kl_ctrl));
|
|
DO_FIELD("kl_shift", TO_INT(state.kl_shift));
|
|
DO_FIELD("kl_alt", TO_INT(state.kl_alt));
|
|
DO_FIELD("kl_super", TO_INT(state.kl_super));
|
|
DO_FIELD("kr_ctrl", TO_INT(state.kr_ctrl));
|
|
DO_FIELD("kr_shift", TO_INT(state.kr_shift));
|
|
DO_FIELD("kr_alt", TO_INT(state.kr_alt));
|
|
DO_FIELD("kr_super", TO_INT(state.kr_super));
|
|
DO_FIELD("kbd_esc_buf", TO_INT(state.kbd_esc_buf));
|
|
} break;
|
|
}
|
|
|
|
krk_runtimeError(vm.exceptions->attributeError, "no attribute '%s'", AS_CSTRING(argv[1]));
|
|
return NONE_VAL();
|
|
}
|
|
#undef DO_FIELD
|
|
#undef TO_INT_
|
|
#undef GET
|
|
#undef TO_INT
|
|
#undef WID
|
|
|
|
static KrkValue _yutani_repr(int argc, const KrkValue argv[], int hasKw) {
|
|
struct YutaniClass * self = (struct YutaniClass*)AS_INSTANCE(argv[0]);
|
|
char out[500];
|
|
size_t len = sprintf(out, "Yutani(fd=%d,server=%s,display_width=%d,display_height=%d)",
|
|
fileno(self->yctx->sock),
|
|
self->yctx->server_ident,
|
|
(int)self->yctx->display_width,
|
|
(int)self->yctx->display_height);
|
|
return OBJECT_VAL(krk_copyString(out,len));
|
|
}
|
|
|
|
static KrkValue _yutani_init(int argc, const KrkValue argv[], int hasKw) {
|
|
if (yctxInstance) {
|
|
krk_runtimeError(vm.exceptions->valueError, "class 'Yutani' is a singleton and has already been initialized.");
|
|
return NONE_VAL();
|
|
}
|
|
|
|
KrkInstance * self = AS_INSTANCE(argv[0]);
|
|
|
|
/* Connect and let's go. */
|
|
yutani_t * yctx = yutani_init();
|
|
if (!yctx) {
|
|
krk_runtimeError(vm.exceptions->ioError, "Failed to connect to compositor.");
|
|
return NONE_VAL();
|
|
}
|
|
|
|
init_decorations();
|
|
|
|
((struct YutaniClass*)self)->yctx = yctx;
|
|
yctxInstance = self;
|
|
krk_attachNamedObject(&module->fields, "_yutani_t", (KrkObj*)self);
|
|
|
|
return argv[0];
|
|
}
|
|
|
|
#define CHECK_YUTANI() \
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], Yutani)) \
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected Yutani"); \
|
|
struct YutaniClass * self = (struct YutaniClass*)AS_INSTANCE(argv[0])
|
|
|
|
static KrkValue _yutani_display_width(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_YUTANI();
|
|
return INTEGER_VAL(self->yctx->display_width);
|
|
}
|
|
|
|
static KrkValue _yutani_display_height(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_YUTANI();
|
|
return INTEGER_VAL(self->yctx->display_height);
|
|
}
|
|
|
|
static KrkValue _yutani_poll(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_YUTANI();
|
|
|
|
int sync = (argc > 1 && IS_BOOLEAN(argv[1])) ? AS_BOOLEAN(argv[1]) : 1;
|
|
yutani_msg_t * result;
|
|
if (sync) {
|
|
result = yutani_poll(self->yctx);
|
|
} else {
|
|
result = yutani_poll_async(self->yctx);
|
|
}
|
|
|
|
if (!result) return NONE_VAL();
|
|
|
|
KrkInstance * out = krk_newInstance(Message);
|
|
krk_push(OBJECT_VAL(out));
|
|
((struct MessageClass*)out)->msg = result;
|
|
krk_attachNamedValue(&out->fields, "type", INTEGER_VAL(result->type));
|
|
|
|
return krk_pop();
|
|
}
|
|
|
|
static KrkValue _yutani_wait_for(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_YUTANI();
|
|
if (argc != 2 || !IS_INTEGER(argv[1])) { krk_runtimeError(vm.exceptions->argumentError, "expected int for msgtype"); return NONE_VAL(); }
|
|
yutani_msg_t * result = yutani_wait_for(self->yctx, AS_INTEGER(argv[1]));
|
|
KrkInstance * out = krk_newInstance(Message);
|
|
krk_push(OBJECT_VAL(out));
|
|
((struct MessageClass*)out)->msg = result;
|
|
krk_attachNamedValue(&out->fields, "type", INTEGER_VAL(result->type));
|
|
|
|
return krk_pop();
|
|
}
|
|
|
|
static KrkValue _yutani_subscribe(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_YUTANI();
|
|
yutani_subscribe_windows(self->yctx);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _yutani_unsubscribe(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_YUTANI();
|
|
yutani_unsubscribe_windows(self->yctx);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _yutani_query_windows(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_YUTANI();
|
|
yutani_query_windows(self->yctx);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _yutani_fileno(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_YUTANI();
|
|
return INTEGER_VAL(fileno(self->yctx->sock));
|
|
}
|
|
|
|
static KrkValue _yutani_query(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_YUTANI();
|
|
return INTEGER_VAL(yutani_query(self->yctx));
|
|
}
|
|
|
|
static KrkValue _yutani_menu_process_event(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_YUTANI();
|
|
if (argc < 2 || !krk_isInstanceOf(argv[1],Message))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected Message");
|
|
struct MessageClass* msg = (struct MessageClass*)AS_INSTANCE(argv[1]);
|
|
|
|
return INTEGER_VAL(menu_process_event(self->yctx, msg->msg));
|
|
}
|
|
|
|
#define GET_ARG(p,name,type) do { \
|
|
if (hasKw && krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S(#name)), &name)) { \
|
|
if (!krk_isInstanceOf(name,type)) \
|
|
return krk_runtimeError(vm.exceptions->typeError, #name " argument should be " #type ", not '%s'", krk_typeName(name)); \
|
|
} else if (argc > p) { \
|
|
name = argv[p]; \
|
|
if (!krk_isInstanceOf(name,type)) \
|
|
return krk_runtimeError(vm.exceptions->typeError, #name " argument should be " #type ", not '%s'", krk_typeName(name)); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define GFX_PROPERTY(name) \
|
|
static KrkValue _gfx_ ## name (int argc, const KrkValue argv[], int hasKw) { \
|
|
if (argc != 1 || !krk_isInstanceOf(argv[0], GraphicsContext)) \
|
|
return krk_runtimeError(vm.exceptions->typeError, "Expected GraphicsContext"); \
|
|
struct GraphicsContext * self = (struct GraphicsContext *)AS_INSTANCE(argv[0]); \
|
|
return INTEGER_VAL(self->ctx-> name); \
|
|
}
|
|
|
|
GFX_PROPERTY(width);
|
|
GFX_PROPERTY(height);
|
|
|
|
#define CHECK_GFX() \
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], GraphicsContext)) \
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected GraphicsContext"); \
|
|
struct GraphicsContext * self = (struct GraphicsContext*)AS_INSTANCE(argv[0])
|
|
|
|
static KrkValue _gfx_fill(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_GFX();
|
|
if (argc < 2 || !krk_isInstanceOf(argv[1], YutaniColor))
|
|
return krk_runtimeError(vm.exceptions->typeError, "fill() takes one color() argument");
|
|
struct YutaniColor * color = (struct YutaniColor*)AS_INSTANCE(argv[1]);
|
|
draw_fill(self->ctx, color->color);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _gfx_flip(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_GFX();
|
|
if (self->doubleBuffered) {
|
|
flip(self->ctx);
|
|
}
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _gfx_blur(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_GFX();
|
|
int radius = 2;
|
|
if (argc > 1 && IS_INTEGER(argv[1])) radius = AS_INTEGER(argv[1]);
|
|
else if (argc > 1) return krk_runtimeError(vm.exceptions->typeError, "expected int");
|
|
blur_context_box(self->ctx, radius);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _gfx_line(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_GFX();
|
|
if (argc < 6 ||
|
|
!IS_INTEGER(argv[1]) ||
|
|
!IS_INTEGER(argv[2]) ||
|
|
!IS_INTEGER(argv[3]) ||
|
|
!IS_INTEGER(argv[4]) ||
|
|
!krk_isInstanceOf(argv[5], YutaniColor)) {
|
|
return krk_runtimeError(vm.exceptions->typeError, "line() expects 4 ints and a color");
|
|
}
|
|
|
|
int32_t x0 = AS_INTEGER(argv[1]);
|
|
int32_t x1 = AS_INTEGER(argv[2]);
|
|
int32_t y0 = AS_INTEGER(argv[3]);
|
|
int32_t y1 = AS_INTEGER(argv[4]);
|
|
struct YutaniColor * color = (struct YutaniColor*)AS_INSTANCE(argv[5]);
|
|
|
|
if (argc > 6) {
|
|
if (IS_INTEGER(argv[6])) {
|
|
draw_line_thick(self->ctx,x0,x1,y0,y1,color->color,AS_INTEGER(argv[6]));
|
|
} else if (IS_FLOATING(argv[6])) {
|
|
draw_line_aa(self->ctx,x0,x1,y0,y1,color->color,AS_FLOATING(argv[6]));
|
|
} else {
|
|
return krk_runtimeError(vm.exceptions->typeError, "thickness must be int or float, not '%s'", krk_typeName(argv[6]));
|
|
}
|
|
} else {
|
|
draw_line(self->ctx,x0,x1,y0,y1,color->color);
|
|
}
|
|
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _gfx_rect(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_GFX();
|
|
|
|
if (argc != 6 ||
|
|
!IS_INTEGER(argv[1]) ||
|
|
!IS_INTEGER(argv[2]) ||
|
|
!IS_INTEGER(argv[3]) ||
|
|
!IS_INTEGER(argv[4]) ||
|
|
!krk_isInstanceOf(argv[5], YutaniColor)) {
|
|
return krk_runtimeError(vm.exceptions->typeError, "rect() expects 4 ints and a color");
|
|
}
|
|
|
|
int32_t x = AS_INTEGER(argv[1]);
|
|
int32_t y = AS_INTEGER(argv[2]);
|
|
uint16_t width = AS_INTEGER(argv[3]);
|
|
uint16_t height = AS_INTEGER(argv[4]);
|
|
struct YutaniColor * color = (struct YutaniColor*)AS_INSTANCE(argv[5]);
|
|
|
|
KrkValue solid = BOOLEAN_VAL(0), radius = NONE_VAL();
|
|
if (hasKw) {
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("solid")), &solid);
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("radius")), &radius);
|
|
}
|
|
|
|
if (!IS_BOOLEAN(solid))
|
|
return krk_runtimeError(vm.exceptions->typeError, "solid must be bool");
|
|
if (!IS_NONE(radius) && !IS_INTEGER(radius))
|
|
return krk_runtimeError(vm.exceptions->typeError, "radius must be int");
|
|
if (!IS_NONE(radius) && AS_BOOLEAN(solid))
|
|
return krk_runtimeError(vm.exceptions->typeError, "radius and solid can not be used together");
|
|
|
|
if (AS_BOOLEAN(solid)) {
|
|
draw_rectangle_solid(self->ctx, x, y, width, height, color->color);
|
|
} else if (IS_INTEGER(radius)) {
|
|
draw_rounded_rectangle(self->ctx, x, y, width, height, AS_INTEGER(radius), color->color);
|
|
} else {
|
|
draw_rectangle(self->ctx, x, y, width, height, color->color);
|
|
}
|
|
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _gfx_draw_sprite(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_GFX();
|
|
|
|
if (argc < 2 || !krk_isInstanceOf(argv[1], YutaniSprite))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected Sprite");
|
|
|
|
if (argc < 4 || !IS_INTEGER(argv[2]) || !IS_INTEGER(argv[3]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected integer coordinate pair");
|
|
|
|
/* Potential kwargs: rotation:float, alpha:float, scale:(int,int)... */
|
|
KrkValue rotation = NONE_VAL(), alpha = NONE_VAL(), scale=NONE_VAL(), color=NONE_VAL();
|
|
if (hasKw) {
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("alpha")), &alpha);
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("rotation")), &rotation);
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("scale")), &scale);
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("color")), &color);
|
|
}
|
|
|
|
if (!IS_NONE(alpha) && !IS_FLOATING(alpha))
|
|
return krk_runtimeError(vm.exceptions->typeError, "alpha must be float");
|
|
if (!IS_NONE(rotation) && !IS_FLOATING(rotation))
|
|
return krk_runtimeError(vm.exceptions->typeError, "rotation must be float");
|
|
if (!IS_NONE(color) && !krk_isInstanceOf(color,YutaniColor))
|
|
return krk_runtimeError(vm.exceptions->typeError, "color must be color");
|
|
if (!IS_NONE(scale) && (!IS_TUPLE(scale) || AS_TUPLE(scale)->values.count != 2 ||
|
|
!IS_INTEGER(AS_TUPLE(scale)->values.values[0]) ||
|
|
!IS_INTEGER(AS_TUPLE(scale)->values.values[1])))
|
|
return krk_runtimeError(vm.exceptions->typeError, "scale must be 2-tuple of ints");
|
|
if (!IS_NONE(rotation) + !IS_NONE(scale) + !IS_NONE(color) > 1)
|
|
return krk_runtimeError(vm.exceptions->typeError, "can not combine rotation / scale / color");
|
|
|
|
if ((!IS_NONE(rotation) || !IS_NONE(color)) && IS_NONE(alpha))
|
|
alpha = FLOATING_VAL(1.0);
|
|
|
|
struct YutaniSprite * sprite = (struct YutaniSprite*)AS_INSTANCE(argv[1]);
|
|
int32_t x = AS_INTEGER(argv[2]);
|
|
int32_t y = AS_INTEGER(argv[3]);
|
|
|
|
if (!IS_NONE(scale)) {
|
|
int32_t width = AS_INTEGER(AS_TUPLE(scale)->values.values[0]);
|
|
int32_t height = AS_INTEGER(AS_TUPLE(scale)->values.values[1]);
|
|
if (IS_NONE(alpha)) {
|
|
draw_sprite_scaled(self->ctx, &sprite->sprite, x, y, width, height);
|
|
} else {
|
|
draw_sprite_scaled_alpha(self->ctx, &sprite->sprite, x, y, width, height, AS_FLOATING(alpha));
|
|
}
|
|
} else if (IS_NONE(alpha)) {
|
|
draw_sprite(self->ctx, &sprite->sprite, x, y);
|
|
} else if (!IS_NONE(color)) {
|
|
draw_sprite_alpha_paint(self->ctx, &sprite->sprite, x, y, AS_FLOATING(alpha), ((struct YutaniColor*)AS_INSTANCE(color))->color);
|
|
} else if (!IS_NONE(rotation)) {
|
|
draw_sprite_rotate(self->ctx, &sprite->sprite, x, y, AS_FLOATING(rotation), AS_FLOATING(alpha));
|
|
} else {
|
|
draw_sprite_alpha(self->ctx, &sprite->sprite, x, y, AS_FLOATING(alpha));
|
|
}
|
|
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static void _sprite_sweep(KrkInstance * self) {
|
|
struct YutaniSprite * sprite = (struct YutaniSprite*)self;
|
|
|
|
if (sprite->sprite.masks) free(sprite->sprite.masks);
|
|
if (sprite->sprite.bitmap) free(sprite->sprite.bitmap);
|
|
if (sprite->ctx) free(sprite->ctx);
|
|
}
|
|
|
|
static KrkValue _sprite_repr(int argc, const KrkValue argv[], int hasKw) {
|
|
struct YutaniSprite * self = (struct YutaniSprite *)AS_INSTANCE(argv[0]);
|
|
|
|
KrkValue file;
|
|
krk_tableGet(&self->inst.fields, OBJECT_VAL(S("file")), &file);
|
|
|
|
char out[500];
|
|
size_t len = sprintf(out, "Sprite('%s',width=%d,height=%d)",
|
|
!IS_STRING(file) ? "" : AS_CSTRING(file),
|
|
(int)self->sprite.width,
|
|
(int)self->sprite.height);
|
|
return OBJECT_VAL(krk_copyString(out,len));
|
|
}
|
|
|
|
static KrkValue _sprite_init(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], YutaniSprite))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected sprite");
|
|
|
|
if (argc < 2 || !IS_STRING(argv[1]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "Sprite() takes one str argument");
|
|
|
|
struct YutaniSprite * self = (struct YutaniSprite*)AS_INSTANCE(argv[0]);
|
|
|
|
int result = load_sprite(&self->sprite, AS_CSTRING(argv[1]));
|
|
if (result) {
|
|
return krk_runtimeError(vm.exceptions->ioError, "Sprite() could not be initialized");
|
|
}
|
|
|
|
self->ctx = init_graphics_sprite(&self->sprite);
|
|
krk_attachNamedValue(&self->inst.fields, "file", argv[1]);
|
|
|
|
return argv[0];
|
|
}
|
|
|
|
#define CHECK_WINDOW() \
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], YutaniWindow)) \
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected Window"); \
|
|
struct WindowClass * self = (struct WindowClass*)AS_INSTANCE(argv[0]); \
|
|
if (!self->window) return krk_runtimeError(vm.exceptions->valueError, "Window is closed")
|
|
|
|
static KrkValue _window_repr(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
KrkValue title;
|
|
krk_tableGet(&self->inst.fields, OBJECT_VAL(S("title")), &title);
|
|
char out[500];
|
|
size_t len = sprintf(out, "Window(wid=%d,title=%s,width=%d,height=%d)",
|
|
self->window->wid,
|
|
IS_NONE(title) ? "" : AS_CSTRING(title),
|
|
(int)self->window->width,
|
|
(int)self->window->height);
|
|
return OBJECT_VAL(krk_copyString(out,len));
|
|
}
|
|
|
|
static KrkValue _window_init(int argc, const KrkValue argv[], int hasKw) {
|
|
if (!yctxInstance) return krk_runtimeError(vm.exceptions->valueError, "Compositor is not initialized");
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], YutaniWindow))
|
|
return krk_runtimeError(vm.exceptions->typeError, "Failed to initialize window");
|
|
|
|
if (argc < 3 || !IS_INTEGER(argv[1]) || !IS_INTEGER(argv[2]))
|
|
return krk_runtimeError(vm.exceptions->argumentError, "Expected at least two (integer) arguments (width, height)");
|
|
|
|
KrkInstance * _self = AS_INSTANCE(argv[0]);
|
|
struct WindowClass * self = (struct WindowClass*)_self;
|
|
krk_integer_type width = AS_INTEGER(argv[1]);
|
|
krk_integer_type height = AS_INTEGER(argv[2]);
|
|
|
|
KrkValue flags = INTEGER_VAL(0), title = NONE_VAL(), icon = NONE_VAL(), doublebuffer = BOOLEAN_VAL(0);
|
|
GET_ARG(3, flags, vm.baseClasses->intClass);
|
|
GET_ARG(4, title, vm.baseClasses->strClass);
|
|
GET_ARG(5, icon, vm.baseClasses->strClass);
|
|
GET_ARG(6, doublebuffer, vm.baseClasses->boolClass);
|
|
|
|
self->window = yutani_window_create_flags(((struct YutaniClass*)yctxInstance)->yctx,
|
|
width, height, AS_INTEGER(flags));
|
|
|
|
self->doubleBuffered = AS_BOOLEAN(doublebuffer);
|
|
|
|
if (self->doubleBuffered) {
|
|
self->ctx = init_graphics_yutani_double_buffer(self->window);
|
|
} else {
|
|
self->ctx = init_graphics_yutani(self->window);
|
|
}
|
|
|
|
if (!IS_NONE(title)) {
|
|
if (!IS_NONE(icon)) {
|
|
yutani_window_advertise_icon(((struct YutaniClass*)yctxInstance)->yctx, self->window, AS_CSTRING(title), AS_CSTRING(icon));
|
|
} else {
|
|
yutani_window_advertise(((struct YutaniClass*)yctxInstance)->yctx, self->window, AS_CSTRING(title));
|
|
}
|
|
}
|
|
|
|
krk_attachNamedValue(&_self->fields, "title", title);
|
|
krk_attachNamedValue(&_self->fields, "icon", icon);
|
|
krk_attachNamedValue(&_self->fields, "closed", BOOLEAN_VAL(0));
|
|
|
|
return argv[0];
|
|
}
|
|
|
|
static KrkValue _window_flip(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (self->doubleBuffered) {
|
|
flip(self->ctx);
|
|
}
|
|
yutani_flip(((struct YutaniClass*)yctxInstance)->yctx, self->window);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_move(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 3 || !IS_INTEGER(argv[1]) || !IS_INTEGER(argv[2]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected two integer arguments");
|
|
yutani_window_move(((struct YutaniClass*)yctxInstance)->yctx, self->window, AS_INTEGER(argv[1]), AS_INTEGER(argv[2]));
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_set_focused(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 2 || !IS_INTEGER(argv[1]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected integer argument");
|
|
self->window->focused = AS_INTEGER(argv[1]);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_close(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
yutani_close(((struct YutaniClass*)yctxInstance)->yctx, self->window);
|
|
self->window = NULL;
|
|
release_graphics_yutani(self->ctx);
|
|
self->ctx = NULL;
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_set_stack(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 2 || !IS_INTEGER(argv[1])) return krk_runtimeError(vm.exceptions->typeError, "expected int for z-order");
|
|
int z = AS_INTEGER(argv[1]);
|
|
yutani_set_stack(((struct YutaniClass*)yctxInstance)->yctx, self->window, z);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_update_shape(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 2 || !IS_INTEGER(argv[1])) return krk_runtimeError(vm.exceptions->typeError, "expected int for shape specifier");
|
|
int set_shape = AS_INTEGER(argv[1]);
|
|
yutani_window_update_shape(((struct YutaniClass*)yctxInstance)->yctx, self->window, set_shape);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_warp_mouse(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 3 || !IS_INTEGER(argv[1]) || !IS_INTEGER(argv[2]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected two int values for x, y");
|
|
int32_t x = AS_INTEGER(argv[1]);
|
|
int32_t y = AS_INTEGER(argv[2]);
|
|
yutani_window_warp_mouse(((struct YutaniClass*)yctxInstance)->yctx, self->window, x, y);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_show_mouse(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 2 || !IS_INTEGER(argv[1])) return krk_runtimeError(vm.exceptions->typeError, "expected int for show_mouse");
|
|
int show_mouse = AS_INTEGER(argv[1]);
|
|
yutani_window_show_mouse(((struct YutaniClass*)yctxInstance)->yctx, self->window, show_mouse);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_resize_start(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 2 || !IS_INTEGER(argv[1])) return krk_runtimeError(vm.exceptions->typeError, "expected int for direction");
|
|
yutani_scale_direction_t direction = AS_INTEGER(argv[1]);
|
|
yutani_window_resize_start(((struct YutaniClass*)yctxInstance)->yctx, self->window, direction);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_resize(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 3 || !IS_INTEGER(argv[1]) || !IS_INTEGER(argv[2]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected two int values for width, height");
|
|
uint32_t width = AS_INTEGER(argv[1]);
|
|
uint32_t height = AS_INTEGER(argv[2]);
|
|
yutani_window_resize(((struct YutaniClass*)yctxInstance)->yctx, self->window, width, height);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_resize_offer(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 3 || !IS_INTEGER(argv[1]) || !IS_INTEGER(argv[2]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected two int values for width, height");
|
|
uint32_t width = AS_INTEGER(argv[1]);
|
|
uint32_t height = AS_INTEGER(argv[2]);
|
|
yutani_window_resize_offer(((struct YutaniClass*)yctxInstance)->yctx, self->window, width, height);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_resize_accept(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 3 || !IS_INTEGER(argv[1]) || !IS_INTEGER(argv[2]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected two int values for width, height");
|
|
uint32_t width = AS_INTEGER(argv[1]);
|
|
uint32_t height = AS_INTEGER(argv[2]);
|
|
yutani_window_resize_accept(((struct YutaniClass*)yctxInstance)->yctx, self->window, width, height);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_resize_done(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
yutani_window_resize_done(((struct YutaniClass*)yctxInstance)->yctx, self->window);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_advertise(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 2 || !IS_STRING(argv[1]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected string for title");
|
|
if (argc > 2 && !IS_STRING(argv[2]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected string for icon");
|
|
|
|
if (argc > 2) {
|
|
yutani_window_advertise_icon(((struct YutaniClass*)yctxInstance)->yctx, self->window, AS_CSTRING(argv[1]), AS_CSTRING(argv[2]));
|
|
} else {
|
|
yutani_window_advertise(((struct YutaniClass*)yctxInstance)->yctx, self->window, AS_CSTRING(argv[1]));
|
|
}
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_special_request(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
if (argc < 2 || !IS_INTEGER(argv[1])) return krk_runtimeError(vm.exceptions->typeError, "expected int for request");
|
|
uint32_t request = AS_INTEGER(argv[1]);
|
|
yutani_special_request(((struct YutaniClass*)yctxInstance)->yctx, self->window, request);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _window_reinit(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_WINDOW();
|
|
reinit_graphics_yutani(self->ctx, self->window);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
#define WINDOW_PROPERTY(name) \
|
|
static KrkValue _window_ ## name (int argc, const KrkValue argv[], int hasKw) { \
|
|
if (argc != 1 || !krk_isInstanceOf(argv[0], YutaniWindow)) \
|
|
return krk_runtimeError(vm.exceptions->typeError, "Expected Window"); \
|
|
struct WindowClass * self = (struct WindowClass*)AS_INSTANCE(argv[0]); \
|
|
return INTEGER_VAL(self->window-> name); \
|
|
}
|
|
|
|
WINDOW_PROPERTY(wid);
|
|
WINDOW_PROPERTY(x);
|
|
WINDOW_PROPERTY(y);
|
|
WINDOW_PROPERTY(focused);
|
|
|
|
static KrkValue _decor_get_bounds(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc > 0 && !krk_isInstanceOf(argv[0], YutaniWindow))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected window");
|
|
struct decor_bounds bounds;
|
|
|
|
decor_get_bounds((argc > 0) ? ((struct WindowClass*)AS_INSTANCE(argv[0]))->window : NULL,
|
|
&bounds);
|
|
|
|
KrkValue result = krk_dict_of(0, NULL, 0);
|
|
krk_push(result);
|
|
|
|
#define SET(val) krk_attachNamedValue(AS_DICT(result), #val, INTEGER_VAL(bounds. val));
|
|
|
|
SET(top_height);
|
|
SET(bottom_height);
|
|
SET(left_width);
|
|
SET(right_width);
|
|
SET(width);
|
|
SET(height);
|
|
|
|
return krk_pop();
|
|
}
|
|
|
|
static KrkValue _decor_handle_event(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], Message))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected message");
|
|
return INTEGER_VAL(decor_handle_event(((struct YutaniClass*)yctxInstance)->yctx, ((struct MessageClass*)AS_INSTANCE(argv[0]))->msg));
|
|
}
|
|
|
|
static KrkValue _decor_render(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], YutaniWindow))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected window");
|
|
char * title = (argc > 1 && IS_STRING(argv[1])) ? AS_CSTRING(argv[1]) : NULL;
|
|
if (title == NULL) {
|
|
KrkValue winTitle;
|
|
if (!krk_tableGet(&AS_INSTANCE(argv[0])->fields, OBJECT_VAL(S("title")), &winTitle) || !IS_STRING(winTitle)) {
|
|
title = "";
|
|
} else {
|
|
title = AS_CSTRING(winTitle);
|
|
}
|
|
}
|
|
render_decorations(((struct WindowClass*)AS_INSTANCE(argv[0]))->window,
|
|
((struct WindowClass*)AS_INSTANCE(argv[0]))->ctx, title);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _decor_show_default_menu(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], YutaniWindow))
|
|
return krk_runtimeError(vm.exceptions->typeError, "show_default_menu() expects Window");
|
|
if (argc < 3 || !IS_INTEGER(argv[1]) || !IS_INTEGER(argv[2]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "show_default_menu() expects int coordinate pair");
|
|
|
|
struct WindowClass * window = (void*)AS_INSTANCE(argv[0]);
|
|
int32_t x = AS_INTEGER(argv[1]);
|
|
int32_t y = AS_INTEGER(argv[2]);
|
|
|
|
decor_show_default_menu(window->window, x, y);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _yutani_color_init(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 4 || !IS_INTEGER(argv[1]) || !IS_INTEGER(argv[2]) || !IS_INTEGER(argv[3]) ||
|
|
(argc > 5) || (argc == 5 && !IS_INTEGER(argv[4]))) return krk_runtimeError(vm.exceptions->typeError, "color() expects three or four integer arguments");
|
|
if (!krk_isInstanceOf(argv[0], YutaniColor)) return krk_runtimeError(vm.exceptions->typeError, "expected color [__init__], not '%s'", krk_typeName(argv[0]));
|
|
struct YutaniColor * self = (struct YutaniColor*)AS_INSTANCE(argv[0]);
|
|
if (argc == 5) {
|
|
self->color = rgba(AS_INTEGER(argv[1]),AS_INTEGER(argv[2]),AS_INTEGER(argv[3]),AS_INTEGER(argv[4]));
|
|
} else {
|
|
self->color = rgb(AS_INTEGER(argv[1]),AS_INTEGER(argv[2]),AS_INTEGER(argv[3]));
|
|
}
|
|
return argv[0];
|
|
}
|
|
|
|
static KrkValue _yutani_color_repr(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc != 1 || !krk_isInstanceOf(argv[0], YutaniColor)) return krk_runtimeError(vm.exceptions->typeError, "expected color [__repr__], not '%s'", krk_typeName(argv[0]));
|
|
struct YutaniColor * self = (struct YutaniColor*)AS_INSTANCE(argv[0]);
|
|
char tmp[30];
|
|
if (_ALP(self->color) != 255) {
|
|
sprintf(tmp, "color<#%02x%02x%02x%02x>", (int)_RED(self->color), (int)_GRE(self->color), (int)_BLU(self->color), (int)_ALP(self->color));
|
|
} else {
|
|
sprintf(tmp, "color<#%02x%02x%02x>", (int)_RED(self->color), (int)_GRE(self->color), (int)_BLU(self->color));
|
|
}
|
|
return OBJECT_VAL(krk_copyString(tmp,strlen(tmp)));
|
|
}
|
|
|
|
static KrkValue _yutani_color_str(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc != 1 || !krk_isInstanceOf(argv[0], YutaniColor)) return krk_runtimeError(vm.exceptions->typeError, "expected color [__str__], not '%s'", krk_typeName(argv[0]));
|
|
struct YutaniColor * self = (struct YutaniColor*)AS_INSTANCE(argv[0]);
|
|
char tmp[30];
|
|
if (_ALP(self->color) != 255) {
|
|
sprintf(tmp, "#%02x%02x%02x%02x", (int)_RED(self->color), (int)_GRE(self->color), (int)_BLU(self->color), (int)_ALP(self->color));
|
|
} else {
|
|
sprintf(tmp, "#%02x%02x%02x", (int)_RED(self->color), (int)_GRE(self->color), (int)_BLU(self->color));
|
|
}
|
|
return OBJECT_VAL(krk_copyString(tmp,strlen(tmp)));
|
|
}
|
|
|
|
#define CHECK_FONT() \
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], YutaniFont)) \
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected Font"); \
|
|
struct YutaniFont * self = (struct YutaniFont*)AS_INSTANCE(argv[0])
|
|
|
|
static KrkValue _font_init(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_FONT();
|
|
|
|
if (argc < 2 || !IS_STRING(argv[1]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected string for font name");
|
|
if (argc < 3 || !IS_INTEGER(argv[2]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected int for font size");
|
|
|
|
KrkValue fontColor = NONE_VAL();
|
|
if (hasKw) {
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("color")), &fontColor);
|
|
if (!krk_isInstanceOf(fontColor, YutaniColor)) return krk_runtimeError(vm.exceptions->typeError, "expected color");
|
|
}
|
|
|
|
/* Try to be smart about this */
|
|
if (strstr(AS_CSTRING(argv[1]), "sans-serif") == AS_CSTRING(argv[1]) || strstr(AS_CSTRING(argv[1]), "monospace") == AS_CSTRING(argv[1])) {
|
|
self->fontData = tt_font_from_shm(AS_CSTRING(argv[1]));
|
|
} else {
|
|
self->fontData = tt_font_from_file(AS_CSTRING(argv[1]));
|
|
}
|
|
|
|
if (!self->fontData)
|
|
return krk_runtimeError(vm.exceptions->typeError, "failed to load font");
|
|
|
|
self->fontSize = AS_INTEGER(argv[2]);
|
|
tt_set_size(self->fontData, self->fontSize);
|
|
|
|
self->fontColor = IS_NONE(fontColor) ? rgb(0,0,0) : ((struct YutaniColor*)AS_INSTANCE(fontColor))->color;
|
|
|
|
return argv[0];
|
|
}
|
|
|
|
static KrkValue _font_size(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_FONT();
|
|
return INTEGER_VAL(self->fontSize);
|
|
}
|
|
|
|
static KrkValue _font_set_size(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_FONT();
|
|
if (argc < 2 || !IS_INTEGER(argv[1]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected int for font size");
|
|
self->fontSize = AS_INTEGER(argv[1]);
|
|
tt_set_size(self->fontData, self->fontSize);
|
|
return INTEGER_VAL(self->fontSize);
|
|
}
|
|
|
|
static KrkValue _font_draw_string(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_FONT();
|
|
if (argc < 2 || !krk_isInstanceOf(argv[1], GraphicsContext))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected GraphicsContext");
|
|
if (argc < 3 || !IS_STRING(argv[2]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected str");
|
|
if (argc < 5 || !IS_INTEGER(argv[3]) || !IS_INTEGER(argv[4]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected int coordinate pair");
|
|
|
|
gfx_context_t * ctx = ((struct GraphicsContext*)AS_INSTANCE(argv[1]))->ctx;
|
|
const char * str = AS_CSTRING(argv[2]);
|
|
int32_t x = AS_INTEGER(argv[3]);
|
|
int32_t y = AS_INTEGER(argv[4]);
|
|
|
|
return INTEGER_VAL(tt_draw_string(ctx, self->fontData, x, y, str, self->fontColor));
|
|
}
|
|
|
|
static KrkValue _font_width(int argc, const KrkValue argv[], int hasKw) {
|
|
CHECK_FONT();
|
|
if (argc < 2 || !IS_STRING(argv[1]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected str");
|
|
|
|
const char * str = AS_CSTRING(argv[1]);
|
|
return INTEGER_VAL(tt_string_width(self->fontData, str));
|
|
}
|
|
|
|
static void _MenuBar_gcsweep(KrkInstance * _self) {
|
|
struct MenuBarClass * self = (struct MenuBarClass*)_self;
|
|
if (self->menuBar.entries) {
|
|
for (size_t i = 0; self->menuBar.entries[i].title; ++i) {
|
|
free(self->menuBar.entries[i].title);
|
|
free(self->menuBar.entries[i].action);
|
|
}
|
|
free(self->menuBar.entries);
|
|
}
|
|
}
|
|
|
|
static void _menubar_callback(struct menu_bar * _self) {
|
|
struct MenuBarClass * self = _self->_private;
|
|
KrkValue callback;
|
|
if (krk_tableGet(&self->inst.fields, OBJECT_VAL(S("callback")), &callback)) {
|
|
krk_push(callback);
|
|
krk_push(OBJECT_VAL(self));
|
|
krk_callStack(1);
|
|
}
|
|
}
|
|
|
|
static KrkValue _MenuBar_init(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], MenuBarClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected MenuBar");
|
|
if (argc < 2 || !IS_TUPLE(argv[1]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected tuple of tuples");
|
|
|
|
struct MenuBarClass * self = (struct MenuBarClass*)AS_INSTANCE(argv[0]);
|
|
self->menuBar.entries = malloc(sizeof(struct menu_bar_entries) * (AS_TUPLE(argv[1])->values.count + 1));
|
|
for (size_t i = 0; i < AS_TUPLE(argv[1])->values.count; ++i) {
|
|
if (!IS_TUPLE(AS_TUPLE(argv[1])->values.values[i]) ||
|
|
AS_TUPLE(AS_TUPLE(argv[1])->values.values[i])->values.count != 2 ||
|
|
!IS_STRING(AS_TUPLE(AS_TUPLE(argv[1])->values.values[i])->values.values[0]) ||
|
|
!IS_STRING(AS_TUPLE(AS_TUPLE(argv[1])->values.values[i])->values.values[1])) {
|
|
return krk_runtimeError(vm.exceptions->typeError, "invalid menu bar entry: expected (str,str) but %d is '%s'",
|
|
(int)i, krk_typeName(AS_TUPLE(argv[1])->values.values[i]));
|
|
}
|
|
|
|
KrkString * title = AS_STRING(AS_TUPLE(AS_TUPLE(argv[1])->values.values[i])->values.values[0]);
|
|
KrkString * action = AS_STRING(AS_TUPLE(AS_TUPLE(argv[1])->values.values[i])->values.values[1]);
|
|
|
|
self->menuBar.entries[i].title = strdup(title->chars);
|
|
self->menuBar.entries[i].action = strdup(action->chars);
|
|
}
|
|
self->menuBar.entries[AS_TUPLE(argv[1])->values.count].title = NULL;
|
|
self->menuBar.entries[AS_TUPLE(argv[1])->values.count].action = NULL;
|
|
|
|
self->menuBar.set = menu_set_create();
|
|
self->menuBar._private = self;
|
|
self->menuBar.redraw_callback = _menubar_callback;
|
|
|
|
krk_attachNamedValue(&self->inst.fields, "entries", argv[1]);
|
|
|
|
/* Give ourselves a dict to track the same information */
|
|
KrkValue dict = krk_dict_of(0,NULL, 0);
|
|
krk_attachNamedValue(&self->inst.fields, "set", dict);
|
|
|
|
return argv[0];
|
|
}
|
|
|
|
static KrkValue _MenuBar_place(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], MenuBarClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected MenuBar");
|
|
struct MenuBarClass * self = (struct MenuBarClass*)AS_INSTANCE(argv[0]);
|
|
if (argc < 4 || !IS_INTEGER(argv[1]) || !IS_INTEGER(argv[2]) || !IS_INTEGER(argv[3]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected int for x, y, width");
|
|
if (argc < 5 || !krk_isInstanceOf(argv[4],YutaniWindow))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected Window");
|
|
|
|
self->menuBar.x = AS_INTEGER(argv[1]);
|
|
self->menuBar.y = AS_INTEGER(argv[2]);
|
|
self->menuBar.width = AS_INTEGER(argv[3]);
|
|
self->menuBar.window = ((struct WindowClass*)AS_INSTANCE(argv[4]))->window;
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _MenuBar_render(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], MenuBarClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected MenuBar");
|
|
struct MenuBarClass * self = (struct MenuBarClass*)AS_INSTANCE(argv[0]);
|
|
if (argc < 2 || !krk_isInstanceOf(argv[1], GraphicsContext))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected GraphicsContext");
|
|
menu_bar_render(&self->menuBar, ((struct GraphicsContext*)AS_INSTANCE(argv[1]))->ctx);
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _MenuBar_mouse_event(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], MenuBarClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected MenuBar");
|
|
struct MenuBarClass * self = (struct MenuBarClass*)AS_INSTANCE(argv[0]);
|
|
if (argc < 3 || !krk_isInstanceOf(argv[1], YutaniWindow) ||
|
|
!krk_isInstanceOf(argv[2], Message))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected Window and Message");
|
|
|
|
struct MessageClass * msg = ((struct MessageClass*)AS_INSTANCE(argv[2]));
|
|
struct yutani_msg_window_mouse_event * me = (struct yutani_msg_window_mouse_event*)msg->msg->data;
|
|
|
|
return INTEGER_VAL(menu_bar_mouse_event(((struct YutaniClass*)yctxInstance)->yctx,
|
|
((struct WindowClass*)AS_INSTANCE(argv[1]))->window,
|
|
&self->menuBar, me, me->new_x, me->new_y));
|
|
}
|
|
|
|
static KrkValue _MenuBar_insert(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], MenuBarClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected MenuBar");
|
|
struct MenuBarClass * self = (struct MenuBarClass*)AS_INSTANCE(argv[0]);
|
|
if (argc < 3 || !IS_STRING(argv[1]) || !krk_isInstanceOf(argv[2], MenuListClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected str and MenuList");
|
|
|
|
menu_set_insert(self->menuBar.set, AS_CSTRING(argv[1]), ((struct MenuListClass*)AS_INSTANCE(argv[2]))->menuList);
|
|
|
|
/* Also assign it to our dict */
|
|
KrkValue dict = NONE_VAL();
|
|
krk_tableGet(&self->inst.fields, OBJECT_VAL(S("set")), &dict);
|
|
if (IS_NONE(dict) || !krk_isInstanceOf(dict,vm.baseClasses->dictClass))
|
|
return krk_runtimeError(vm.exceptions->baseException, "Failed to get set entries?");
|
|
krk_tableSet(AS_DICT(dict), argv[1], argv[2]);
|
|
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static KrkValue _MenuList_init(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], MenuListClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected MenuList");
|
|
struct MenuListClass * self = (struct MenuListClass*)AS_INSTANCE(argv[0]);
|
|
self->menuList = menu_create();
|
|
|
|
/* Give us a list to put entries in for GC tracking and retrieval by kuroko code */
|
|
KrkValue list = krk_list_of(0,NULL,0);
|
|
krk_attachNamedValue(&self->inst.fields, "entries", list);
|
|
|
|
return argv[0];
|
|
}
|
|
|
|
static KrkValue _MenuList_insert(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], MenuListClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected MenuList");
|
|
struct MenuListClass * self = (struct MenuListClass*)AS_INSTANCE(argv[0]);
|
|
if (argc < 2 || !krk_isInstanceOf(argv[1], MenuEntryClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "Expected MenuEntry");
|
|
|
|
/* Append to menu */
|
|
menu_insert(self->menuList, ((struct MenuEntryClass*)AS_INSTANCE(argv[1]))->menuEntry);
|
|
|
|
/* Append to internal list */
|
|
KrkValue list = NONE_VAL();
|
|
krk_tableGet(&self->inst.fields, OBJECT_VAL(S("entries")), &list);
|
|
if (IS_NONE(list) || !krk_isInstanceOf(list,vm.baseClasses->listClass))
|
|
return krk_runtimeError(vm.exceptions->baseException, "Failed to get entries?");
|
|
krk_writeValueArray(AS_LIST(list), argv[1]);
|
|
|
|
return NONE_VAL();
|
|
}
|
|
|
|
static void _MenuEntry_callback_internal(struct MenuEntry * _self) {
|
|
struct MenuEntryClass * self = (struct MenuEntryClass *)_self->_private;
|
|
KrkValue callback = NONE_VAL();
|
|
krk_tableGet(&self->inst.fields, OBJECT_VAL(S("callback")), &callback);
|
|
krk_push(callback);
|
|
krk_push(OBJECT_VAL(self));
|
|
krk_callStack(1);
|
|
}
|
|
|
|
static KrkValue _MenuEntry_init(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], MenuEntryClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected MenuEntry");
|
|
struct MenuEntryClass * self = (struct MenuEntryClass*)AS_INSTANCE(argv[0]);
|
|
|
|
if (argc < 3 || !IS_STRING(argv[1]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected title and callback");
|
|
|
|
KrkValue icon = NONE_VAL(), action = NONE_VAL();
|
|
if (hasKw) {
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("icon")), &icon);
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("action")), &action);
|
|
if (!IS_NONE(icon) && !IS_STRING(icon))
|
|
return krk_runtimeError(vm.exceptions->typeError, "icon must be str, not '%s'", krk_typeName(icon));
|
|
if (!IS_NONE(action) && !IS_STRING(action))
|
|
return krk_runtimeError(vm.exceptions->typeError, "action must be str, not '%s'", krk_typeName(action));
|
|
}
|
|
|
|
self->menuEntry = menu_create_normal(
|
|
IS_STRING(icon) ? AS_CSTRING(icon) : NULL,
|
|
IS_STRING(action) ? AS_CSTRING(action) : NULL,
|
|
AS_CSTRING(argv[1]),
|
|
_MenuEntry_callback_internal);
|
|
|
|
self->menuEntry->_private = self;
|
|
|
|
krk_attachNamedValue(&self->inst.fields, "callback", argv[2]);
|
|
|
|
return argv[0];
|
|
}
|
|
|
|
/* TODO properties: icon, action, title */
|
|
|
|
static KrkValue _MenuEntrySubmenu_init(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], MenuEntrySubmenuClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected MenuEntrySubmenu");
|
|
|
|
struct MenuEntryClass * self = (struct MenuEntryClass*)AS_INSTANCE(argv[0]);
|
|
|
|
if (argc < 2 || !IS_STRING(argv[1]))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected title to be a str");
|
|
|
|
KrkValue icon = NONE_VAL(), action = NONE_VAL();
|
|
if (hasKw) {
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("icon")), &icon);
|
|
krk_tableGet(AS_DICT(argv[argc]), OBJECT_VAL(S("action")), &action);
|
|
if (!IS_NONE(icon) && !IS_STRING(icon))
|
|
return krk_runtimeError(vm.exceptions->typeError, "icon must be str, not '%s'", krk_typeName(icon));
|
|
if (!IS_NONE(action) && !IS_STRING(action))
|
|
return krk_runtimeError(vm.exceptions->typeError, "action must be str, not '%s'", krk_typeName(action));
|
|
}
|
|
|
|
self->menuEntry = menu_create_submenu(
|
|
IS_STRING(icon) ? AS_CSTRING(icon) : NULL,
|
|
IS_STRING(action) ? AS_CSTRING(action) : NULL,
|
|
AS_CSTRING(argv[1]));
|
|
|
|
self->menuEntry->_private = self;
|
|
|
|
return argv[0];
|
|
}
|
|
|
|
static KrkValue _MenuEntrySeparator_init(int argc, const KrkValue argv[], int hasKw) {
|
|
if (argc < 1 || !krk_isInstanceOf(argv[0], MenuEntrySeparatorClass))
|
|
return krk_runtimeError(vm.exceptions->typeError, "expected MenuEntrySeparator");
|
|
struct MenuEntryClass * self = (struct MenuEntryClass*)AS_INSTANCE(argv[0]);
|
|
self->menuEntry = menu_create_separator();
|
|
self->menuEntry->_private = self;
|
|
return argv[0];
|
|
}
|
|
|
|
KrkValue krk_module_onload__yutani(void) {
|
|
module = krk_newInstance(vm.baseClasses->moduleClass);
|
|
/* Store it on the stack for now so we can do stuff that may trip GC
|
|
* and not lose it to garbage colletion... */
|
|
krk_push(OBJECT_VAL(module));
|
|
|
|
/**
|
|
* class Message(object):
|
|
* MSG_... = ... # Directly from the library headers.
|
|
*/
|
|
Message = krk_createClass(module, "Message", NULL);
|
|
Message->allocSize = sizeof(struct MessageClass);
|
|
Message->_ongcsweep = _message_sweep;
|
|
/* All the MSG_ constants */
|
|
#define TYPE(type) krk_attachNamedValue(&Message->methods, "MSG_" #type, INTEGER_VAL(YUTANI_MSG_ ## type))
|
|
TYPE(HELLO); TYPE(WINDOW_NEW); TYPE(FLIP); TYPE(KEY_EVENT); TYPE(MOUSE_EVENT);
|
|
TYPE(WINDOW_MOVE); TYPE(WINDOW_CLOSE); TYPE(WINDOW_SHOW); TYPE(WINDOW_HIDE);
|
|
TYPE(WINDOW_STACK); TYPE(WINDOW_FOCUS_CHANGE); TYPE(WINDOW_MOUSE_EVENT);
|
|
TYPE(FLIP_REGION); TYPE(WINDOW_NEW_FLAGS); TYPE(RESIZE_REQUEST);
|
|
TYPE(RESIZE_OFFER); TYPE(RESIZE_ACCEPT); TYPE(RESIZE_BUFID); TYPE(RESIZE_DONE);
|
|
TYPE(WINDOW_ADVERTISE); TYPE(SUBSCRIBE); TYPE(UNSUBSCRIBE); TYPE(NOTIFY);
|
|
TYPE(QUERY_WINDOWS); TYPE(WINDOW_FOCUS); TYPE(WINDOW_DRAG_START); TYPE(WINDOW_WARP_MOUSE);
|
|
TYPE(WINDOW_SHOW_MOUSE); TYPE(WINDOW_RESIZE_START); TYPE(SESSION_END);
|
|
TYPE(KEY_BIND); TYPE(WINDOW_UPDATE_SHAPE); TYPE(CLIPBOARD); TYPE(GOODBYE);
|
|
TYPE(SPECIAL_REQUEST); TYPE(WELCOME); TYPE(WINDOW_INIT);
|
|
#undef TYPE
|
|
/* Structure bindings */
|
|
krk_defineNative(&Message->methods, "__getattr__", _message_getattr);
|
|
krk_finalizeClass(Message);
|
|
|
|
/**
|
|
* class color():
|
|
* rgb(a) value for use with graphics functions.
|
|
*/
|
|
YutaniColor = krk_createClass(module, "color", NULL);
|
|
YutaniColor->allocSize = sizeof(struct YutaniColor);
|
|
YutaniColor->docstring = S("color(r,g,b,a=255)\n Representation of an RGB(A) color.");
|
|
krk_defineNative(&YutaniColor->methods, "__init__", _yutani_color_init);
|
|
krk_defineNative(&YutaniColor->methods, "__repr__", _yutani_color_repr);
|
|
krk_defineNative(&YutaniColor->methods, "__str__", _yutani_color_str);
|
|
krk_finalizeClass(YutaniColor);
|
|
|
|
/**
|
|
* class Yutani(object):
|
|
* yctx = yutani_t *
|
|
* display_width = yctx->display_width
|
|
* display_height = yctx->display_height
|
|
*
|
|
* def __init__(self): # Call yutani_init()
|
|
*/
|
|
Yutani = krk_createClass(module, "Yutani", NULL);
|
|
Yutani->allocSize = sizeof(struct YutaniClass);
|
|
Yutani->docstring = S("Yutani()\n Establish a connection to the compositor display server.");
|
|
krk_defineNativeProperty(&Yutani->methods, "display_width", _yutani_display_width);
|
|
krk_defineNativeProperty(&Yutani->methods, "display_height", _yutani_display_height);
|
|
krk_defineNative(&Yutani->methods, "__repr__", _yutani_repr);
|
|
krk_defineNative(&Yutani->methods, "__init__", _yutani_init);
|
|
krk_defineNative(&Yutani->methods, "poll", _yutani_poll);
|
|
krk_defineNative(&Yutani->methods, "wait_for", _yutani_wait_for);
|
|
krk_defineNative(&Yutani->methods, "subscribe", _yutani_subscribe);
|
|
krk_defineNative(&Yutani->methods, "unsubscribe", _yutani_unsubscribe);
|
|
krk_defineNative(&Yutani->methods, "query_windows", _yutani_query_windows);
|
|
krk_defineNative(&Yutani->methods, "fileno", _yutani_fileno);
|
|
krk_defineNative(&Yutani->methods, "query", _yutani_query);
|
|
krk_defineNative(&Yutani->methods, "menu_process_event", _yutani_menu_process_event);
|
|
#if 0
|
|
krk_defineNative(&Yutani->methods, "focus_window", _yutani_focus_window);
|
|
krk_defineNative(&Yutani->methods, "session_end", _yutani_session_end);
|
|
krk_defineNative(&Yutani->methods, "key_bind", _yutani_key_bind);
|
|
#endif
|
|
krk_finalizeClass(Yutani);
|
|
|
|
/**
|
|
* class GraphicsContext():
|
|
* ctx = gfx_context_t *
|
|
*/
|
|
GraphicsContext = krk_createClass(module, "GraphicsContext", NULL);
|
|
GraphicsContext->allocSize = sizeof(struct GraphicsContext);
|
|
krk_defineNativeProperty(&GraphicsContext->methods, "width", _gfx_width);
|
|
krk_defineNativeProperty(&GraphicsContext->methods, "height", _gfx_height);
|
|
krk_defineNative(&GraphicsContext->methods, "fill", _gfx_fill)->doc =
|
|
"GraphicsContext.fill(color)\n"
|
|
" Fill the entire context with the given color.";
|
|
krk_defineNative(&GraphicsContext->methods, "flip", _gfx_flip)->doc =
|
|
"GraphicsContext.flip()\n"
|
|
" If the context is double-buffered, flip its backbuffer.";
|
|
krk_defineNative(&GraphicsContext->methods, "blur", _gfx_blur)->doc =
|
|
"GraphicsContext.blur(radius=2)\n"
|
|
" Perform an in-place box blur on this graphics context.";
|
|
krk_defineNative(&GraphicsContext->methods, "line", _gfx_line)->doc =
|
|
"GraphicsContext.line(x0,x1,y0,y1,color,thickness=None)\n"
|
|
" Draw a line between the given points. If thickness is not provided, uses a\n"
|
|
" a simple Bresenham algorithm. If thickness is an int, draws with a box-shaped pen.\n"
|
|
" If thickness is a float, draws using a point-distance antialiasing algorithm.";
|
|
krk_defineNative(&GraphicsContext->methods, "rect", _gfx_rect)->doc =
|
|
"GraphicsContext.rect(x,y,width,height,color,solid=False,radius=None)\n"
|
|
" Draw a filled rectangle. If solid is True, paints the given color directly to\n"
|
|
" the underlying backbuffer with no alpha calculations. If radius is provided,\n"
|
|
" draws a rounded rectangle.";
|
|
krk_defineNative(&GraphicsContext->methods, "draw_sprite", _gfx_draw_sprite)->doc =
|
|
"GraphicsContext.draw_sprite(sprite,x,y,alpha=None,rotation=None,scale=None,color=None)\n"
|
|
" Blit a sprite to this graphics context at the given coordinates.\n"
|
|
" alpha: float of opacity; 1.0 = fully opaque (default)\n"
|
|
" rotation: float of radians; when a rotation is given, the coordinates provided are\n"
|
|
" the center of the rendered sprite, rather than the upper left corner.\n"
|
|
" scale: (int,int) of final resolution of sprite; can not be used with rotation.\n"
|
|
" color: color to paint the sprite as, can not be used with rotation or scale;\n"
|
|
" used to paint a given color with this sprite as a 'brush'. Useful for\n"
|
|
" colored icons, such as those found in the panel.";
|
|
krk_finalizeClass(GraphicsContext);
|
|
|
|
/**
|
|
* class Window(GraphicsContext):
|
|
* ctx = gfx_context_t *
|
|
* window = yutani_window_t *
|
|
*/
|
|
YutaniWindow = krk_createClass(module, "Window", GraphicsContext);
|
|
YutaniWindow->allocSize = sizeof(struct WindowClass);
|
|
YutaniWindow->docstring = S("Window(width,height,flags=0,title=None,icon=None,doublebuffer=False)\n"
|
|
" Create a new window and initializes a graphics rendering context for it.");
|
|
krk_defineNative(&YutaniWindow->methods, "__repr__", _window_repr);
|
|
krk_defineNative(&YutaniWindow->methods, "__init__", _window_init);
|
|
krk_defineNative(&YutaniWindow->methods, "flip", _window_flip);
|
|
krk_defineNative(&YutaniWindow->methods, "move", _window_move);
|
|
krk_defineNative(&YutaniWindow->methods, "set_focused", _window_set_focused);
|
|
krk_defineNative(&YutaniWindow->methods, "close", _window_close);
|
|
krk_defineNative(&YutaniWindow->methods, "set_stack", _window_set_stack);
|
|
krk_defineNative(&YutaniWindow->methods, "special_request", _window_special_request);
|
|
krk_defineNative(&YutaniWindow->methods, "resize", _window_resize);
|
|
krk_defineNative(&YutaniWindow->methods, "resize_start", _window_resize_start);
|
|
krk_defineNative(&YutaniWindow->methods, "resize_done", _window_resize_done);
|
|
krk_defineNative(&YutaniWindow->methods, "resize_offer", _window_resize_offer);
|
|
krk_defineNative(&YutaniWindow->methods, "resize_accept", _window_resize_accept);
|
|
krk_defineNative(&YutaniWindow->methods, "update_shape", _window_update_shape);
|
|
krk_defineNative(&YutaniWindow->methods, "show_mouse", _window_show_mouse);
|
|
krk_defineNative(&YutaniWindow->methods, "warp_mouse", _window_warp_mouse);
|
|
krk_defineNative(&YutaniWindow->methods, "set_stack", _window_set_stack);
|
|
krk_defineNative(&YutaniWindow->methods, "advertise", _window_advertise);
|
|
krk_defineNative(&YutaniWindow->methods, "reinit", _window_reinit);
|
|
|
|
krk_defineNativeProperty(&YutaniWindow->methods, "wid", _window_wid);
|
|
krk_defineNativeProperty(&YutaniWindow->methods, "x", _window_x);
|
|
krk_defineNativeProperty(&YutaniWindow->methods, "y", _window_y);
|
|
krk_defineNativeProperty(&YutaniWindow->methods, "focused", _window_focused);
|
|
krk_finalizeClass(YutaniWindow);
|
|
|
|
/**
|
|
* class Sprite(GraphicsContext):
|
|
* ctx = gfx_context_t *
|
|
* sprite = sprite_t
|
|
*/
|
|
YutaniSprite = krk_createClass(module, "Sprite", GraphicsContext);
|
|
YutaniSprite->allocSize = sizeof(struct YutaniSprite);
|
|
YutaniSprite->_ongcsweep = _sprite_sweep;
|
|
YutaniSprite->docstring = S("Sprite(filename)\n Create a sprite from the requested texture file.");
|
|
krk_defineNative(&YutaniSprite->methods, "__repr__", _sprite_repr);
|
|
krk_defineNative(&YutaniSprite->methods, "__init__", _sprite_init);
|
|
krk_finalizeClass(YutaniSprite);
|
|
|
|
/**
|
|
* class Font():
|
|
* fontType, fontSize, fontGamma, fontStroke
|
|
*/
|
|
YutaniFont = krk_createClass(module, "Font", NULL);
|
|
YutaniFont->allocSize = sizeof(struct YutaniFont);
|
|
YutaniFont->docstring = S("Font(type,size,gamma=1.7,stroke=0.75,color=color(0,0,0))\n"
|
|
" Create a Font specification for rendering text.");
|
|
krk_defineNative(&YutaniFont->methods, "__init__", _font_init);
|
|
krk_defineNative(&YutaniFont->methods, "draw_string", _font_draw_string)->doc =
|
|
"Font.draw_string(gfxContext, string, x, y)\n"
|
|
" Draw text to a graphics context with this font.";
|
|
krk_defineNative(&YutaniFont->methods, "width", _font_width)->doc =
|
|
"Font.width(string)\n"
|
|
" Calculate the rendered width of the given string when drawn with this font.";
|
|
krk_defineNativeProperty(&YutaniFont->methods, "size", _font_size);
|
|
krk_defineNative(&YutaniFont->methods, "set_size", _font_set_size);
|
|
krk_finalizeClass(YutaniFont);
|
|
|
|
MenuBarClass = krk_createClass(module, "MenuBar", NULL);
|
|
MenuBarClass->allocSize = sizeof(struct MenuBarClass);
|
|
MenuBarClass->_ongcsweep = _MenuBar_gcsweep;
|
|
krk_defineNative(&MenuBarClass->methods, "__init__", _MenuBar_init);
|
|
krk_defineNative(&MenuBarClass->methods, "place", _MenuBar_place);
|
|
krk_defineNative(&MenuBarClass->methods, "render", _MenuBar_render);
|
|
krk_defineNative(&MenuBarClass->methods, "mouse_event", _MenuBar_mouse_event);
|
|
krk_defineNative(&MenuBarClass->methods, "insert", _MenuBar_insert);
|
|
krk_finalizeClass(MenuBarClass);
|
|
|
|
MenuListClass = krk_createClass(module, "MenuList", NULL);
|
|
MenuListClass->allocSize = sizeof(struct MenuListClass);
|
|
krk_defineNative(&MenuListClass->methods, "__init__", _MenuList_init);
|
|
krk_defineNative(&MenuListClass->methods, "insert", _MenuList_insert);
|
|
krk_finalizeClass(MenuListClass);
|
|
|
|
MenuEntryClass = krk_createClass(module, "MenuEntry", NULL);
|
|
MenuEntryClass->allocSize = sizeof(struct MenuEntryClass);
|
|
krk_defineNative(&MenuEntryClass->methods, "__init__", _MenuEntry_init);
|
|
krk_finalizeClass(MenuEntryClass);
|
|
|
|
MenuEntrySubmenuClass = krk_createClass(module, "MenuEntrySubmenu", MenuEntryClass);
|
|
krk_defineNative(&MenuEntrySubmenuClass->methods, "__init__", _MenuEntrySubmenu_init);
|
|
krk_finalizeClass(MenuEntrySubmenuClass);
|
|
MenuEntrySeparatorClass = krk_createClass(module, "MenuEntrySeparator", MenuEntryClass);
|
|
krk_defineNative(&MenuEntrySeparatorClass->methods, "__init__", _MenuEntrySeparator_init);
|
|
krk_finalizeClass(MenuEntrySeparatorClass);
|
|
|
|
KrkInstance * Decorator = krk_newInstance(vm.baseClasses->objectClass);
|
|
krk_attachNamedObject(&module->fields, "Decorator", (KrkObj*)Decorator);
|
|
krk_defineNative(&Decorator->fields, "get_bounds", _decor_get_bounds);
|
|
krk_defineNative(&Decorator->fields, "render", _decor_render);
|
|
krk_defineNative(&Decorator->fields, "handle_event", _decor_handle_event);
|
|
krk_defineNative(&Decorator->fields, "show_default_menu", _decor_show_default_menu);
|
|
#define ATTACH_CONSTANT(name) krk_attachNamedValue(&Decorator->fields, #name, INTEGER_VAL(name))
|
|
ATTACH_CONSTANT(DECOR_OTHER);
|
|
ATTACH_CONSTANT(DECOR_CLOSE);
|
|
ATTACH_CONSTANT(DECOR_RESIZE);
|
|
ATTACH_CONSTANT(DECOR_MAXIMIZE);
|
|
ATTACH_CONSTANT(DECOR_RIGHT);
|
|
|
|
ATTACH_CONSTANT(DECOR_ACTIVE);
|
|
ATTACH_CONSTANT(DECOR_INACTIVE);
|
|
|
|
ATTACH_CONSTANT(DECOR_FLAG_DECORATED);
|
|
ATTACH_CONSTANT(DECOR_FLAG_NO_MAXIMIZE);
|
|
ATTACH_CONSTANT(DECOR_FLAG_TILED);
|
|
ATTACH_CONSTANT(DECOR_FLAG_TILE_LEFT);
|
|
ATTACH_CONSTANT(DECOR_FLAG_TILE_RIGHT);
|
|
ATTACH_CONSTANT(DECOR_FLAG_TILE_UP);
|
|
ATTACH_CONSTANT(DECOR_FLAG_TILE_DOWN);
|
|
#undef ATTACH_CONSTANT
|
|
|
|
/* Pop the module object before returning; it'll get pushed again
|
|
* by the VM before the GC has a chance to run, so it's safe. */
|
|
assert(AS_INSTANCE(krk_pop()) == module);
|
|
return OBJECT_VAL(module);
|
|
}
|
|
|