shithub: guifs

ref: c51962a648b3f38fc378ad0081b1d1aa037142d5
dir: guifs/event.c

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <thread.h>

#include "guifs.h"

GuiElement *
elementat(Point p)
{
	GuiElement *g = nil;

	rlock(&root->lock);
	if(ptinrect(p, root->rect))
		g = root;
	runlock(&root->lock);

	while(g){
		GuiElement *next = nil;
		rlock(&g->lock);
		for(int n = 0; n < g->nchildren && next == nil; n++){
			GuiElement *child = g->children[n];
			rlock(&child->lock);
			if(ptinrect(p, child->rect)) /* TODO: does not account for floating elements (not a thing yet) */
				next = child;
			runlock(&child->lock);
		}
		runlock(&g->lock);
		if(next)
			g = next;
		else
			break;
	}
	return g;
}

typedef struct SendEvent SendEvent;
struct SendEvent {
	Channel *chan;
	char *event;
};

void
sendeventthread(void *arg)
{
	SendEvent *s = arg;
	send(s->chan, &s->event);
}

void
sendevent(GuiElement *g, Event event)
{
	SendEvent *s = emalloc(sizeof(SendEvent));
	s->chan = g->events;

	Event *e = emalloc(sizeof(Event));
	memcpy(e, &event, sizeof(Event));

	switch(e->type){
	case Xmousedown:
		s->event = smprint("mousedown %C\n", e->r);
		break;
	case Xmouseup:
		s->event = smprint("mouseup %C\n", e->r);
		break;
	case Xmouseclick:
		s->event = smprint("mouseclick %C\n", e->r);
		break;
	case Xmousescroll:
		s->event = smprint("mousescroll %s\n", e->direction == Up ? "up" : "down");
		break;
	case Xmenuhit:
		s->event = smprint("menuhit %c %d %s\n", e->hit.button, e->hit.which, e->hit.text);
		break;
	case Xkeyboard:
		s->event = smprint("key %C\n", e->r);
		break;
	default:
		s->event = smprint("???\n");
		break;
	}

	if(nbsend(s->chan, &s->event) == 0)
		threadcreate(sendeventthread, s, 1024);
}

int
mouseevent(Mouse m)
{
	GuiElement *g = elementat(mousexy);

	static int lastbuttons = 0;
	static GuiElement *lastL = nil;
	static GuiElement *lastM = nil;
	static GuiElement *lastR = nil;

	int down = 0;

	int b = lastbuttons ^ m.buttons;
	lastbuttons = m.buttons;
	if(b&4 && m.buttons&4){
		lastR = g;
		down = 3;
	}
	if(b&2 && m.buttons&2){
		lastM = g;
		down = 2;
	}
	if(b&1 && m.buttons&1){
		lastL = g;
		down = 1;
	}

	if(!g)
		return 0;

	wlock(&g->lock);
	Event e;
	MenuSpec *ms = getprop(g, Pmenus, 0).menus;
	if(down >= 1 && down <= 3 && ms->menus[down-1] != nil){
		int which = menuhit(down, mousectl, ms->menus[down-1], nil);
		e.type = Xmenuhit;
		e.hit.button = (down == 1) ? 'L' : (down == 2) ? 'M' : 'R';
		e.hit.which = which;
		e.hit.text = ms->menus[down-1]->item[which];
		if(g->listening && which != -1)
			sendevent(g, e);
		wunlock(&g->lock);

		switch(down){
		case 1: lastL = nil; break;
		case 2: lastM = nil; break;
		case 3: lastR = nil; break;
		}
		lastbuttons = lastbuttons ^ (1<<(down-1));
		
		return 1;
	}

	if(!g->listening){
		wunlock(&g->lock);
		return 0;
	}

	b = g->buttons ^ m.buttons;
	g->buttons = m.buttons;

	if(b&16 && m.buttons&16){
		e.type = Xmousescroll;
		e.direction = Down;
		sendevent(g, e);
	}

	if(b&8 && m.buttons&8){
		e.type = Xmousescroll;
		e.direction = Up;
		sendevent(g, e);
	}

	if(b&4){
		e.type = (m.buttons&4) ? Xmousedown : Xmouseup;
		e.r = 'R';
		sendevent(g, e);
		if(e.type == Xmouseup){
			if(lastR == g){
				e.type = Xmouseclick;
				sendevent(g, e);
			}
			lastR = nil;
		}
	}

	if(b&2){
		e.type = (m.buttons&2) ? Xmousedown : Xmouseup;
		e.r = 'M';
		sendevent(g, e);
		if(e.type == Xmouseup){
			if(lastM == g){
				e.type = Xmouseclick;
				sendevent(g, e);
			}
			lastM = nil;
		}
	}

	if(b&1){
		e.type = (m.buttons&1) ? Xmousedown : Xmouseup;
		e.r = 'L';
		sendevent(g, e);
		if(e.type == Xmouseup){
			if(lastL == g){
				e.type = Xmouseclick;
				sendevent(g, e);
			}
			lastL = nil;
		}
	}

	wunlock(&g->lock);
	return 0;
}

int
keyboardevent(Rune r)
{
	if(r == Kdel){ /* The user hit DEL */
		postnote(PNGROUP, getpid(), "interrupt");
		exits("interrupt");
	}

	GuiElement *g = elementat(mousexy);
	if(!g)
		return 0;

	wlock(&g->lock);
	if(g->listening){
		Event e;
		e.type = Xkeyboard;
		e.r = r;
		sendevent(g, e);
	}

	if(g->type == Gtextbox){
		PropVal val = getprop(g, Ptext, 0);
		long len = runestrlen(val.text);
		Rune *text = emalloc(sizeof(Rune) * (len + 2));
		memcpy(text, val.text, sizeof(Rune) * len);
		text[len] = r;
		val.text = text;
		setprop(g, Ptext, val, 0);
	}
	wunlock(&g->lock);
	return 1;
}