Files
multirom_m86/lib/notification_card.c
KINGbabasula 55600f4acc Use a macro for unused variables
Change-Id: I491bea842274029feb965b71c3a62b5953c2ce5c
2015-03-23 14:57:20 +01:00

569 lines
14 KiB
C

/*
* This file is part of MultiROM.
*
* MultiROM 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, either version 3 of the License, or
* (at your option) any later version.
*
* MultiROM 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 MultiROM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "util.h"
#include "animation.h"
#include "framebuffer.h"
#include "notification_card.h"
#include "containers.h"
#include "log.h"
#include "input.h"
#include "colors.h"
enum
{
LEVEL_NCARD_SHADOW = 49,
LEVEL_NCARD_BG = 50,
LEVEL_NCARD_BTN_HOVER = 55,
LEVEL_NCARD_TEXT = 60,
LEVEL_NCARD_CENTER_OFFSET = 1000,
};
#define CARD_PADDING_H (40*DPI_MUL)
#define CARD_PADDING_V (30*DPI_MUL)
#define CARD_MARGIN (40*DPI_MUL)
#define CARD_WIDTH (fb_width - CARD_MARGIN*2)
#define CARD_SHADOW_OFF (7*DPI_MUL)
ncard_builder *ncard_create_builder(void)
{
return mzalloc(sizeof(ncard_builder));
}
void ncard_set_title(ncard_builder *b, const char *title)
{
b->title = realloc(b->title, strlen(title)+1);
strcpy(b->title, title);
}
void ncard_set_text(ncard_builder *b, const char *text)
{
b->text = realloc(b->text, strlen(text)+1);
strcpy(b->text, text);
}
void ncard_set_pos(ncard_builder *b, int pos)
{
b->pos = pos;
}
void ncard_set_cancelable(ncard_builder *b, int cancelable)
{
b->cancelable = cancelable;
}
void ncard_avoid_item(ncard_builder *b, void *item)
{
fb_item_pos *it = item;
b->avoid_item = mzalloc(sizeof(fb_item_pos));
b->avoid_item->x = it->x;
b->avoid_item->y = it->y;
b->avoid_item->w = it->w;
b->avoid_item->h = it->h;
}
void ncard_add_btn(ncard_builder *b, int btn_type, const char *text, ncard_callback callback, void *callback_data)
{
if(b->buttons[btn_type])
{
free(b->buttons[btn_type]->text);
free(b->buttons[btn_type]);
}
ncard_builder_btn *btn = mzalloc(sizeof(ncard_builder_btn));
btn->text = strtoupper(text);
btn->callback_data = callback_data;
btn->callback = callback;
b->buttons[btn_type] = btn;
}
void ncard_set_on_hidden(ncard_builder *b, ncard_callback callback, void *data)
{
b->on_hidden_call = callback;
b->on_hidden_data = data;
}
void ncard_set_from_black(ncard_builder *b, int from_black)
{
b->reveal_from_black = from_black;
}
static int ncard_calc_pos(ncard_builder* b, int max_y)
{
if(b->pos == NCARD_POS_AUTO)
{
if(!b->avoid_item)
return NCARD_POS_TOP;
if(b->avoid_item->y > max_y)
return NCARD_POS_TOP;
return NCARD_POS_BOTTOM;
}
else
return b->pos;
}
struct ncard_btn
{
fb_item_pos pos;
void *callback_data;
ncard_callback callback;
};
struct ncard
{
fb_rect *bg;
fb_rect *shadow;
fb_rect *alpha_bg;
fb_text **texts;
fb_rect *hover_rect;
struct ncard_btn btns[BTN_COUNT];
int active_btns;
int pos;
int targetH;
int top_offset;
int last_y;
int hiding;
int touch_handler_registered;
int touch_id;
int hover_btn;
int cancelable;
ncard_callback on_hidden_call;
void *on_hidden_data;
int reveal_from_black;
pthread_mutex_t mutex;
} ncard = {
.bg = NULL,
.shadow = NULL,
.texts = NULL,
.active_btns = 0,
.top_offset = 0,
.hiding = 0,
.hover_rect = NULL,
.touch_handler_registered = 0,
.touch_id = -1,
.hover_btn = 0,
.cancelable = 0,
.on_hidden_call = NULL,
.on_hidden_data = NULL,
.reveal_from_black = 0,
.mutex = PTHREAD_MUTEX_INITIALIZER,
};
static int ncard_touch_handler(touch_event *ev, void *data)
{
struct ncard *c = data;
pthread_mutex_lock(&c->mutex);
if(c->touch_id == -1 && (ev->changed & TCHNG_ADDED))
{
int i;
for(i = 0; i < BTN_COUNT; ++i)
{
if(!(c->active_btns & (1 << i)))
continue;
if(in_rect(ev->x, ev->y, c->btns[i].pos.x, c->btns[i].pos.y, c->btns[i].pos.w, c->btns[i].pos.h))
{
fb_rm_rect(c->hover_rect);
int level = LEVEL_NCARD_BTN_HOVER;
if(c->pos == NCARD_POS_CENTER)
level += LEVEL_NCARD_CENTER_OFFSET;
c->hover_rect = fb_add_rect_lvl(level, c->btns[i].pos.x, c->btns[i].pos.y, c->btns[i].pos.w, c->btns[i].pos.h, C_NCARD_SHADOW);
c->touch_id = ev->id;
c->hover_btn = i;
break;
}
}
}
if(c->touch_id != ev->id)
{
if(c->cancelable)
{
pthread_mutex_unlock(&c->mutex);
ncard_hide();
return 0;
}
else
{
pthread_mutex_unlock(&c->mutex);
return c->pos == NCARD_POS_CENTER ? 0 : -1;
}
}
if(ev->changed & TCHNG_REMOVED)
{
struct ncard_btn *b = &c->btns[c->hover_btn];
if(b->callback && in_rect(ev->x, ev->y, b->pos.x, b->pos.y, b->pos.w, b->pos.h))
{
ncard_callback call = b->callback;
void *call_data = b->callback_data;
pthread_mutex_unlock(&c->mutex);
call(call_data);
pthread_mutex_lock(&c->mutex);
}
else
{
fb_rm_rect(c->hover_rect);
c->hover_rect = NULL;
}
c->touch_id = -1;
}
pthread_mutex_unlock(&c->mutex);
return 0;
}
static void ncard_move_step(void *data, float interpolated)
{
int i;
struct ncard *c = data;
pthread_mutex_lock(&c->mutex);
const int diff = c->bg->y - c->last_y;
for(i = 0; c->texts && c->texts[i]; ++i)
c->texts[i]->y += diff;
for(i = 0; i < BTN_COUNT; ++i)
{
if(!(c->active_btns & (1 << i)))
continue;
c->btns[i].pos.y += diff;
}
c->shadow->y += diff;
c->shadow->h = c->bg->h;
if(c->hover_rect)
c->hover_rect->y += diff;
c->last_y = c->bg->y;
if(c->alpha_bg && (c->hiding || (c->alpha_bg->color & (0xFF << 24)) != 0xCC000000))
{
if(interpolated > 1.f)
interpolated = 1.f;
if(c->hiding)
interpolated = 1.f - interpolated;
if(!c->hiding && c->reveal_from_black)
c->alpha_bg->color = (c->alpha_bg->color & ~(0xFF << 24)) | ((0xFF - (int)(0x33*interpolated)) << 24);
else
c->alpha_bg->color = (c->alpha_bg->color & ~(0xFF << 24)) | (((int)(0xCC*interpolated)) << 24);
}
pthread_mutex_unlock(&c->mutex);
fb_request_draw();
}
static void ncard_reveal_finished(UNUSED void *data)
{
pthread_mutex_lock(&ncard.mutex);
ncard.bg->h = ncard.targetH;
ncard.shadow->h = ncard.targetH;
pthread_mutex_unlock(&ncard.mutex);
}
static void ncard_hide_finished(void *data)
{
struct ncard *c = data;
list_clear(&c->texts, fb_remove_item);
fb_rm_rect(c->shadow);
fb_rm_rect(c->alpha_bg);
fb_rm_rect(c->hover_rect);
free(c);
}
void ncard_set_top_offset(int top_offset)
{
pthread_mutex_lock(&ncard.mutex);
ncard.top_offset = top_offset;
pthread_mutex_unlock(&ncard.mutex);
}
void ncard_show(ncard_builder *b, int destroy_builder)
{
int i, items_h, btn_x, btn_h, has_btn = 0, it_y = 0, lvl_offset = 0;
fb_text *title = 0, *text = 0, *btns[BTN_COUNT];
int interpolator;
pthread_mutex_lock(&ncard.mutex);
if(ncard.bg)
anim_cancel_for(ncard.bg, 0);
if(b->pos == NCARD_POS_CENTER)
lvl_offset = LEVEL_NCARD_CENTER_OFFSET;
items_h = CARD_PADDING_V*2;
if(b->title)
{
fb_text_proto *p = fb_text_create(CARD_MARGIN + CARD_PADDING_H, fb_height, C_NCARD_TEXT, SIZE_EXTRA, b->title);
p->level = LEVEL_NCARD_TEXT + lvl_offset;
p->style = STYLE_MEDIUM;
p->wrap_w = CARD_WIDTH - CARD_PADDING_H*2;
title = fb_text_finalize(p);
items_h += title->h;
}
if(b->text)
{
fb_text_proto *p = fb_text_create(CARD_MARGIN + CARD_PADDING_H, fb_height, C_NCARD_TEXT_SECONDARY, SIZE_NORMAL, b->text);\
p->level = LEVEL_NCARD_TEXT + lvl_offset;
p->wrap_w = CARD_WIDTH - CARD_PADDING_H*2;
if(!title)
{
p->style = STYLE_ITALIC;
p->justify = JUSTIFY_CENTER;
}
text = fb_text_finalize(p);
items_h += text->h;
}
if(title && text)
items_h += title->h;
ncard.active_btns = 0;
btn_x = CARD_MARGIN + CARD_WIDTH - CARD_PADDING_H;
btn_h = 0;
for(i = 0; i < BTN_COUNT; ++i)
{
if(!b->buttons[i])
continue;
ncard.active_btns |= (1 << i);
fb_text_proto *p = fb_text_create(btn_x, fb_height, C_NCARD_TEXT, SIZE_NORMAL, b->buttons[i]->text);
p->level = LEVEL_NCARD_TEXT + lvl_offset;
p->style = STYLE_MEDIUM;
fb_text *t = fb_text_finalize(p);
t->x -= t->w;
btn_x -= t->w + t->h*2;
btn_h = imax(t->h*2, btn_h);
btns[i] = t;
ncard.btns[i].callback_data = b->buttons[i]->callback_data;
ncard.btns[i].callback = b->buttons[i]->callback;
ncard.btns[i].pos.w = t->w + t->h*2;
ncard.btns[i].pos.h = t->h*3;
ncard.btns[i].pos.x = btn_x + t->h;
}
items_h += btn_h*1.25;
int new_pos = ncard_calc_pos(b, ncard.top_offset + items_h + CARD_MARGIN);
if(new_pos != ncard.pos && ncard.bg)
{
pthread_mutex_unlock(&ncard.mutex);
ncard_hide();
pthread_mutex_lock(&ncard.mutex);
}
ncard.pos = new_pos;
list_clear(&ncard.texts, fb_remove_item);
fb_rm_rect(ncard.hover_rect);
ncard.hover_rect = NULL;
if(!ncard.bg)
{
ncard.bg = fb_add_rect_lvl(LEVEL_NCARD_BG + lvl_offset, CARD_MARGIN, 0, CARD_WIDTH, items_h, C_NCARD_BG);
ncard.bg->y = ncard.pos == NCARD_POS_BOTTOM ? (int)fb_height : -items_h;
ncard.shadow = fb_add_rect_lvl(LEVEL_NCARD_SHADOW + lvl_offset, CARD_MARGIN + CARD_SHADOW_OFF, 0, CARD_WIDTH, items_h, C_NCARD_SHADOW);
interpolator = INTERPOLATOR_OVERSHOOT;
}
else
interpolator = INTERPOLATOR_ACCEL_DECEL;
ncard.targetH = items_h;
if(ncard.pos != NCARD_POS_CENTER)
ncard.targetH *= 1.3;
else if(!ncard.alpha_bg)
{
ncard.alpha_bg = fb_add_rect_lvl(LEVEL_NCARD_SHADOW + lvl_offset - 1, 0, 0, fb_width, fb_height,
b->reveal_from_black ? BLACK : 0x00000000);
}
if(items_h >= ncard.bg->h)
{
ncard.bg->h = ncard.targetH;
ncard.shadow->h = ncard.bg->h;
}
it_y = ncard.bg->y + CARD_PADDING_V;
if(ncard.pos == NCARD_POS_TOP)
it_y += ncard.bg->h - items_h;
if(title)
{
title->y = it_y;
it_y += title->h*1.5;
list_add(&ncard.texts, title);
}
if(text)
{
text->y = it_y;
it_y += text->h + btn_h*0.75;
if(!title)
center_text(text, 0, -1, fb_width, -1);
list_add(&ncard.texts, text);
}
for(i = 0; i < BTN_COUNT; ++i)
{
if(!(ncard.active_btns & (1 << i)))
continue;
btns[i]->y = it_y;
ncard.btns[i].pos.y = it_y - ncard.btns[i].pos.h/3;
list_add(&ncard.texts, btns[i]);
}
if(ncard.active_btns && !ncard.touch_handler_registered)
{
add_touch_handler_async(ncard_touch_handler, &ncard);
ncard.touch_handler_registered = 1;
}
else if(!ncard.active_btns && ncard.touch_handler_registered)
{
rm_touch_handler_async(ncard_touch_handler, &ncard);
ncard.touch_handler_registered = 0;
}
ncard.shadow->y = ncard.pos == NCARD_POS_BOTTOM ? ncard.bg->y - CARD_SHADOW_OFF : ncard.bg->y + CARD_SHADOW_OFF;
ncard.last_y = ncard.bg->y;
ncard.cancelable = b->cancelable;
ncard.on_hidden_call = b->on_hidden_call;
ncard.on_hidden_data = b->on_hidden_data;
ncard.reveal_from_black = b->reveal_from_black;
item_anim *a = item_anim_create(ncard.bg, 400, interpolator);
switch(ncard.pos)
{
case NCARD_POS_TOP:
a->targetY = ncard.top_offset - (ncard.bg->h - items_h);
break;
case NCARD_POS_BOTTOM:
a->targetY = fb_height - items_h;
break;
case NCARD_POS_CENTER:
a->targetY = fb_height/2 - items_h/2;
break;
}
a->targetH = ncard.targetH;
a->on_step_call = ncard_move_step;
a->on_step_data = &ncard;
a->on_finished_call = ncard_reveal_finished;
item_anim_add(a);
pthread_mutex_unlock(&ncard.mutex);
if(destroy_builder)
ncard_destroy_builder(b);
}
void ncard_hide(void)
{
if(!ncard.bg)
return;
anim_cancel_for(ncard.bg, 0);
struct ncard *c = mzalloc(sizeof(struct ncard));
pthread_mutex_lock(&ncard.mutex);
c->bg = ncard.bg;
c->shadow = ncard.shadow;
c->hover_rect = ncard.hover_rect;
c->texts = ncard.texts;
c->last_y = c->bg->y;
c->alpha_bg = ncard.alpha_bg;
c->hiding = 1;
ncard.shadow = NULL;
ncard.hover_rect = NULL;
ncard.bg = NULL;
ncard.texts = NULL;
ncard.alpha_bg = NULL;
if(ncard.touch_handler_registered)
{
rm_touch_handler(ncard_touch_handler, &ncard);
ncard.touch_handler_registered = 0;
}
pthread_mutex_unlock(&ncard.mutex);
item_anim *a = item_anim_create(c->bg, 400, INTERPOLATOR_ACCELERATE);
a->targetY = ncard.pos == NCARD_POS_TOP ? -c->bg->h : (int)fb_height + c->bg->h;
a->destroy_item_when_finished = 1;
a->on_step_call = ncard_move_step;
a->on_step_data = c;
a->on_finished_call = ncard_hide_finished;
a->on_finished_data = c;
item_anim_add(a);
if(ncard.on_hidden_call)
ncard.on_hidden_call(ncard.on_hidden_data);
}
void ncard_hide_callback(UNUSED void *data)
{
ncard_hide();
}
void ncard_destroy_builder(ncard_builder *b)
{
free(b->title);
free(b->text);
free(b->avoid_item);
int i;
for(i = 0; i < BTN_COUNT; ++i)
{
if(b->buttons[i])
{
free(b->buttons[i]->text);
free(b->buttons[i]);
}
}
free(b);
}
int ncard_try_cancel(void)
{
if(ncard.bg && ncard.cancelable)
{
ncard_hide();
return 1;
}
return 0;
}
int ncard_is_visible(void)
{
return ncard.bg != NULL;
}