cocoa!
This commit is contained in:
parent
0b2b8dfdb1
commit
7a0538b2ea
432
vinci-cocoa.m
Normal file
432
vinci-cocoa.m
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
/*
|
||||||
|
* Vinci
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Orastron Srl unipersonale
|
||||||
|
*
|
||||||
|
* Vinci is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3 of the License.
|
||||||
|
*
|
||||||
|
* Vinci is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Vinci. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* File author: Paolo Marrone
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vinci.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface VinciView : NSView
|
||||||
|
{
|
||||||
|
NSTrackingArea *tracking;
|
||||||
|
@public window *win;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface VinciViewController : NSViewController
|
||||||
|
{
|
||||||
|
@public window *win;
|
||||||
|
}
|
||||||
|
@property (nonatomic, strong) VinciView *vinciView;
|
||||||
|
@end
|
||||||
|
|
||||||
|
struct vinci {
|
||||||
|
window *windows;
|
||||||
|
NSApplication *app;
|
||||||
|
NSAutoreleasePool *pool;
|
||||||
|
char standalone; // If any view has a parent, it is not standalone
|
||||||
|
};
|
||||||
|
|
||||||
|
struct window {
|
||||||
|
vinci *g;
|
||||||
|
window *next;
|
||||||
|
NSWindow *nswindow;
|
||||||
|
VinciView *view;
|
||||||
|
VinciViewController *controller;
|
||||||
|
unsigned char *img;
|
||||||
|
NSImage *nsImage;
|
||||||
|
void *data;
|
||||||
|
window_cbs cbs;
|
||||||
|
NSRect default_frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t get_mouse_state(NSEvent* e) {
|
||||||
|
(void) e;
|
||||||
|
return (uint32_t) [NSEvent pressedMouseButtons];
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation VinciView
|
||||||
|
|
||||||
|
- (void) updateTrackingAreas {
|
||||||
|
|
||||||
|
[self removeTrackingArea];
|
||||||
|
|
||||||
|
tracking = [[NSTrackingArea alloc] initWithRect:[self bounds]
|
||||||
|
options:NSTrackingActiveAlways | NSTrackingInVisibleRect | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved
|
||||||
|
owner:self
|
||||||
|
userInfo:nil];
|
||||||
|
[self addTrackingArea:tracking];
|
||||||
|
[super updateTrackingAreas];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) removeTrackingArea {
|
||||||
|
if (tracking) {
|
||||||
|
[super removeTrackingArea:tracking];
|
||||||
|
[tracking release];
|
||||||
|
tracking = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) mouseDown : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||||
|
if (w->cbs.on_mouse_press)
|
||||||
|
w->cbs.on_mouse_press(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) mouseUp : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||||
|
if (w->cbs.on_mouse_release)
|
||||||
|
w->cbs.on_mouse_release(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) mouseMoved : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||||
|
if (w->cbs.on_mouse_move)
|
||||||
|
w->cbs.on_mouse_move(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) rightMouseDown : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||||
|
if (w->cbs.on_mouse_press)
|
||||||
|
w->cbs.on_mouse_press(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) rightMouseUp : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||||
|
if (w->cbs.on_mouse_release)
|
||||||
|
w->cbs.on_mouse_release(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) rightMouseDragged : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||||
|
if (w->cbs.on_mouse_move)
|
||||||
|
w->cbs.on_mouse_move(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) otherMouseDown : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||||
|
if (w->cbs.on_mouse_press)
|
||||||
|
w->cbs.on_mouse_press(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) otherMouseUp : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||||
|
if (w->cbs.on_mouse_release)
|
||||||
|
w->cbs.on_mouse_release(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) otherMouseDragged : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||||
|
if (w->cbs.on_mouse_move)
|
||||||
|
w->cbs.on_mouse_move(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) mouseDragged : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||||
|
if (w->cbs.on_mouse_move)
|
||||||
|
w->cbs.on_mouse_move(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) mouseEntered : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = e.locationInWindow;
|
||||||
|
if (w->cbs.on_mouse_enter)
|
||||||
|
w->cbs.on_mouse_enter(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) mouseExited : (NSEvent *) e {
|
||||||
|
window *w = self->win;
|
||||||
|
NSPoint p = e.locationInWindow;
|
||||||
|
if (w->cbs.on_mouse_leave)
|
||||||
|
w->cbs.on_mouse_leave(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) keyDown : (NSEvent *) e {
|
||||||
|
//window *w = self->win;
|
||||||
|
//NSPoint p = e.locationInWindow;
|
||||||
|
//on_key_press(w, (int32_t)p.x, self.frame.size.height - (int32_t)p.y, get_mouse_state(e));
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) keyUp : (NSEvent *) e {
|
||||||
|
//window *w = self->win;
|
||||||
|
//NSPoint p = e.locationInWindow;
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// custom
|
||||||
|
- (void) vinci_resized {
|
||||||
|
window *w = self->win;
|
||||||
|
NSSize size = [w->view bounds].size;
|
||||||
|
w->img = (unsigned char*)realloc(w->img, (uint32_t) (size.width * size.height) * 4);
|
||||||
|
if (w->cbs.on_window_resize)
|
||||||
|
w->cbs.on_window_resize(w, size.width, size.height);
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeFromSuperview {
|
||||||
|
[super removeFromSuperview];
|
||||||
|
window *w = self->win;
|
||||||
|
if (w->cbs.on_window_close)
|
||||||
|
w->cbs.on_window_close(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom method for standalone application
|
||||||
|
- (void)windowWillClose:(NSNotification *)notification {
|
||||||
|
window *w = self->win;
|
||||||
|
if (w->cbs.on_window_close)
|
||||||
|
w->cbs.on_window_close(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) drawRect:(NSRect) r {
|
||||||
|
[self->win->nsImage drawInRect:r fromRect:r operation:NSCompositingOperationCopy fraction:1.0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation VinciViewController
|
||||||
|
|
||||||
|
- (void)loadView {
|
||||||
|
self.vinciView = [[VinciView alloc] initWithFrame:self->win->default_frame];
|
||||||
|
self.view = self.vinciView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewDidLayout {
|
||||||
|
[super viewDidLayout];
|
||||||
|
[self.vinciView vinci_resized];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
vinci* vinci_new(void) {
|
||||||
|
vinci *g = (vinci*) malloc(sizeof(vinci));
|
||||||
|
if (g == NULL)
|
||||||
|
return NULL;
|
||||||
|
g->windows = NULL;
|
||||||
|
g->pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
g->app = [NSApplication sharedApplication];
|
||||||
|
//[g->app setActivationPolicy:NSApplicationActivationPolicyRegular]; // TODO: check if this is a problem when not standalone
|
||||||
|
g->standalone = 1;
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vinci_destroy(vinci *g) {
|
||||||
|
free(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vinci_idle(vinci *g) {
|
||||||
|
(void) g;
|
||||||
|
|
||||||
|
NSApplication *app = [NSApplication sharedApplication];
|
||||||
|
NSDate *expiration = [NSDate now];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
NSEvent *event = [app nextEventMatchingMask:NSEventMaskAny
|
||||||
|
untilDate:expiration
|
||||||
|
inMode:NSDefaultRunLoopMode
|
||||||
|
dequeue:YES];
|
||||||
|
if (!event)
|
||||||
|
break;
|
||||||
|
[app sendEvent:event];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window* window_new(vinci *g, void *parent, uint32_t width, uint32_t height, window_cbs *cbs) {
|
||||||
|
|
||||||
|
window *ret = (window*) malloc(sizeof(window));
|
||||||
|
if (ret == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ret->default_frame = NSMakeRect(0, 0, width, height);
|
||||||
|
|
||||||
|
VinciViewController *controller = [[VinciViewController alloc] init];
|
||||||
|
controller->win = ret;
|
||||||
|
VinciView *view = (VinciView*) controller.view;
|
||||||
|
|
||||||
|
view->win = ret;
|
||||||
|
|
||||||
|
ret->g = g;
|
||||||
|
ret->view = view;
|
||||||
|
ret->controller = controller;
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
[((NSView*)parent) addSubview:view positioned:NSWindowAbove relativeTo:nil];
|
||||||
|
ret->nswindow = [(NSView*)parent window];
|
||||||
|
g->standalone = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int style = NSWindowStyleMaskClosable | NSWindowStyleMaskResizable | NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable;
|
||||||
|
ret->nswindow = [[NSWindow alloc] initWithContentRect:ret->default_frame
|
||||||
|
styleMask:style
|
||||||
|
backing:NSBackingStoreBuffered
|
||||||
|
defer:NO];
|
||||||
|
[ret->nswindow setTitle:@"Vinci window"];
|
||||||
|
[ret->nswindow setContentView:view];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:view
|
||||||
|
selector:@selector(windowWillClose:)
|
||||||
|
name:NSWindowWillCloseNotification
|
||||||
|
object:ret->nswindow];
|
||||||
|
}
|
||||||
|
|
||||||
|
ret->img = (unsigned char*) malloc(width * height * 4);
|
||||||
|
ret->data = NULL;
|
||||||
|
ret->nsImage = NULL;
|
||||||
|
ret->next = NULL;
|
||||||
|
ret->cbs = *cbs;
|
||||||
|
|
||||||
|
[view autorelease];
|
||||||
|
|
||||||
|
if (g->windows == NULL)
|
||||||
|
g->windows = ret;
|
||||||
|
else {
|
||||||
|
window *cur = g->windows;
|
||||||
|
while (cur->next != NULL)
|
||||||
|
cur = cur->next;
|
||||||
|
cur->next = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_free(window *w) {
|
||||||
|
if (w->g->windows == w) {
|
||||||
|
w->g->windows = w->next;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
window *cur = w->g->windows;
|
||||||
|
while (cur != NULL && cur->next != w)
|
||||||
|
cur = cur->next;
|
||||||
|
if (cur == NULL)
|
||||||
|
return;
|
||||||
|
cur->next = w->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
[w->view removeTrackingArea];
|
||||||
|
free(w->img);
|
||||||
|
free(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_draw(window *w, unsigned char *img, int32_t dx, int32_t dy, int32_t dw, int32_t dh, int32_t wx, int32_t wy, int32_t width, int32_t height) {
|
||||||
|
if (w->img == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
NSSize size = [w->view bounds].size;
|
||||||
|
|
||||||
|
uint32_t iw = wy * size.width + wx;
|
||||||
|
uint32_t o = dy * dw + dx;
|
||||||
|
uint32_t p1 = dw;
|
||||||
|
uint32_t p2 = size.width;
|
||||||
|
|
||||||
|
for (int32_t y = dy; y < dy + height && y < dh; y++) {
|
||||||
|
memcpy(w->img + iw * 4, img + o * 4, width * 4);
|
||||||
|
iw += p2;
|
||||||
|
o += p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGDataProviderRef provider = CGDataProviderCreateWithData(
|
||||||
|
NULL, // void *info
|
||||||
|
w->img, // const void *data
|
||||||
|
size.width * size.height * 4, // size_t size
|
||||||
|
NULL // CGDataProviderReleaseDataCallback releaseData
|
||||||
|
);
|
||||||
|
|
||||||
|
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
|
||||||
|
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
|
||||||
|
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
|
||||||
|
|
||||||
|
CGImageRef imageRef = CGImageCreate(
|
||||||
|
size.width, // size_t width
|
||||||
|
size.height, // size_t height
|
||||||
|
8, // size_t bitsPerComponent
|
||||||
|
4 * 8, // size_t bitsPerPixel
|
||||||
|
size.width * 4, // size_t bytesPerRow
|
||||||
|
colorSpaceRef, // CGColorSpaceRef space
|
||||||
|
bitmapInfo, // CGBitmapInfo bitmapInfo
|
||||||
|
provider, // CGDataProviderRef provider
|
||||||
|
NULL, // const CGFloat *decode
|
||||||
|
NO, // bool shouldInterpolate
|
||||||
|
renderingIntent // CGColorRenderingIntent intent
|
||||||
|
);
|
||||||
|
|
||||||
|
NSImage* image = [[NSImage alloc] initWithCGImage: imageRef size:NSZeroSize];
|
||||||
|
[w->nsImage release];
|
||||||
|
w->nsImage = image;
|
||||||
|
|
||||||
|
[w->view setNeedsDisplayInRect:NSMakeRect(wx, size.height - (wy + height), width, height)];
|
||||||
|
|
||||||
|
CGDataProviderRelease(provider);
|
||||||
|
CGColorSpaceRelease(colorSpaceRef);
|
||||||
|
CGImageRelease(imageRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_resize (window *w, uint32_t width, uint32_t height) {
|
||||||
|
NSSize newSize = NSMakeSize(width, height);
|
||||||
|
[w->view setFrameSize:newSize]; // TODO: this sends NSViewFrameDidChangeNotification, we should handle that instead in 1 place
|
||||||
|
w->img = (unsigned char*)realloc(w->img, width * height * 4);
|
||||||
|
w->view.needsDisplay = YES;
|
||||||
|
if (w->cbs.on_window_resize)
|
||||||
|
w->cbs.on_window_resize(w, width, height); // view should be automatically informed
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_move(window *w, uint32_t x, uint32_t y) {
|
||||||
|
(void) w;
|
||||||
|
(void) x;
|
||||||
|
(void) y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* window_get_handle(window *w) {
|
||||||
|
return w->view;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t window_get_width(window *w) {
|
||||||
|
return [w->view bounds].size.width;
|
||||||
|
}
|
||||||
|
uint32_t window_get_height(window *w) {
|
||||||
|
return [w->view bounds].size.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_show(window *w) {
|
||||||
|
// Can we? nswindow comes from outside
|
||||||
|
[w->nswindow setIsVisible:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_hide(window *w) {
|
||||||
|
(void) w;
|
||||||
|
//[w->nswindow setIsVisible:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
void window_set_data(window *w, void *data) {
|
||||||
|
w->data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *window_get_data(window *w) {
|
||||||
|
return w->data;
|
||||||
|
}
|
4
vinci.h
4
vinci.h
@ -44,7 +44,7 @@ typedef struct window_cbs {
|
|||||||
|
|
||||||
vinci* vinci_new (void);
|
vinci* vinci_new (void);
|
||||||
void vinci_destroy (vinci *g);
|
void vinci_destroy (vinci *g);
|
||||||
void vinci_idle (vinci *g);
|
void vinci_idle (vinci *g); // Non blocking: call it repeatedly with a timer
|
||||||
window* window_new (vinci *g, void* p, uint32_t width, uint32_t height, window_cbs *cbs);
|
window* window_new (vinci *g, void* p, uint32_t width, uint32_t height, window_cbs *cbs);
|
||||||
void window_free (window *w);
|
void window_free (window *w);
|
||||||
void window_draw (window *w, unsigned char *data, int32_t dx, int32_t dy, int32_t dw, int32_t dh, int32_t wx, int32_t wy, int32_t width, int32_t height);
|
void window_draw (window *w, unsigned char *data, int32_t dx, int32_t dy, int32_t dw, int32_t dh, int32_t wx, int32_t wy, int32_t width, int32_t height);
|
||||||
@ -58,4 +58,4 @@ void window_hide (window *w);
|
|||||||
void window_set_data (window *w, void *data);
|
void window_set_data (window *w, void *data);
|
||||||
void* window_get_data (window *w);
|
void* window_get_data (window *w);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user