/* * Copyright © 2008-2010 dragchan * This file is part of FbTerm. * * This program 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 2 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include "imapi.h" #define OFFSET(TYPE, MEMBER) ((size_t)(&(((TYPE *)0)->MEMBER))) #define MSG(a) ((Message *)(a)) static int imfd = -1; static ImCallbacks cbs; static char pending_msg_buf[10240]; static unsigned pending_msg_buf_len = 0; static int im_active = 0; static void wait_message(MessageType type); void register_im_callbacks(ImCallbacks callbacks) { cbs = callbacks; } int get_im_socket() { static char init = 0; if (!init) { init = 1; char *val = getenv("FBTERM_IM_SOCKET"); if (val) { char *tail; int fd = strtol(val, &tail, 0); if (!*tail) imfd = fd; } } return imfd; } void connect_fbterm(char raw) { get_im_socket(); if (imfd == -1) return; Message msg; msg.type = Connect; msg.len = sizeof(msg); msg.raw = (raw ? 1 : 0); int ret = write(imfd, (char *)&msg, sizeof(msg)); } void put_im_text(const char *text, unsigned len) { if (imfd == -1 || !im_active || !text || !len || (OFFSET(Message, texts) + len > UINT16_MAX)) return; char buf[OFFSET(Message, texts) + len]; MSG(buf)->type = PutText; MSG(buf)->len = sizeof(buf); memcpy(MSG(buf)->texts, text, len); int ret = write(imfd, buf, MSG(buf)->len); } void set_im_window(unsigned id, Rectangle rect) { if (imfd == -1 || !im_active || id >= NR_IM_WINS) return; Message msg; msg.type = SetWin; msg.len = sizeof(msg); msg.win.winid = id; msg.win.rect = rect; int ret = write(imfd, (char *)&msg, sizeof(msg)); wait_message(AckWin); } void fill_rect(Rectangle rect, unsigned char color) { Message msg; msg.type = FillRect; msg.len = sizeof(msg); msg.fillRect.rect = rect; msg.fillRect.color = color; int ret = write(imfd, (char *)&msg, sizeof(msg)); } void draw_text(unsigned x, unsigned y, unsigned char fc, unsigned char bc, const char *text, unsigned len) { if (!text || !len) return; char buf[OFFSET(Message, drawText.texts) + len]; MSG(buf)->type = DrawText; MSG(buf)->len = sizeof(buf); MSG(buf)->drawText.x = x; MSG(buf)->drawText.y = y; MSG(buf)->drawText.fc = fc; MSG(buf)->drawText.bc = bc; memcpy(MSG(buf)->drawText.texts, text, len); int ret = write(imfd, buf, MSG(buf)->len); } static int process_message(Message *msg) { int exit = 0; switch (msg->type) { case Disconnect: close(imfd); imfd = -1; exit = 1; break; case FbTermInfo: if (cbs.fbterm_info) { cbs.fbterm_info(&msg->info); } break; case Active: im_active = 1; if (cbs.active) { cbs.active(); } break; case Deactive: if (cbs.deactive) { cbs.deactive(); } im_active = 0; break; case ShowUI: if (im_active && cbs.show_ui) { cbs.show_ui(msg->winid); } break; case HideUI: { if (im_active && cbs.hide_ui) { cbs.hide_ui(); } Message msg; msg.type = AckHideUI; msg.len = sizeof(msg); int ret = write(imfd, (char *)&msg, sizeof(msg)); break; } case SendKey: if (im_active && cbs.send_key) { cbs.send_key(msg->keys, msg->len - OFFSET(Message, keys)); } break; case CursorPosition: if (im_active && cbs.cursor_position) { cbs.cursor_position(msg->cursor.x, msg->cursor.y); } break; case TermMode: if (im_active && cbs.term_mode) { cbs.term_mode(msg->term.crWithLf, msg->term.applicKeypad, msg->term.cursorEscO); } break; default: break; } return exit; } static int process_messages(char *buf, int len) { char *cur = buf, *end = cur + len; int exit = 0; for (; cur < end && MSG(cur)->len <= (end - cur); cur += MSG(cur)->len) { exit |= process_message(MSG(cur)); } return exit; } static void wait_message(MessageType type) { int ack = 0; while (!ack) { char *cur = pending_msg_buf + pending_msg_buf_len; int len = read(imfd, cur, sizeof(pending_msg_buf) - pending_msg_buf_len); if (len == -1 && (errno == EAGAIN || errno == EINTR)) continue; else if (len <= 0) { close(imfd); imfd = -1; return; } pending_msg_buf_len += len; char *end = cur + len; for (; cur < end && MSG(cur)->len <= (end - cur); cur += MSG(cur)->len) { if (MSG(cur)->type == type) { memcpy(cur, cur + MSG(cur)->len, end - cur - MSG(cur)->len); pending_msg_buf_len -= MSG(cur)->len; ack = 1; break; } } } if (pending_msg_buf_len) { Message msg; msg.type = Ping; msg.len = sizeof(msg); int ret = write(imfd, (char *)&msg, sizeof(msg)); } } int check_im_message() { if (imfd == -1) return 0; char buf[sizeof(pending_msg_buf)]; int len, exit = 0; if (pending_msg_buf_len) { len = pending_msg_buf_len; pending_msg_buf_len = 0; memcpy(buf, pending_msg_buf, len); exit |= process_messages(buf, len); } len = read(imfd, buf, sizeof(buf)); if (len == -1 && (errno == EAGAIN || errno == EINTR)) return 1; else if (len <= 0) { close(imfd); imfd = -1; return 0; } exit |= process_messages(buf, len); return !exit; }