Gnome апплет / моя первая программа под nix*

Все началось с того, что я поставил на свой нетбук Ubuntu. После сексуальных извращений с различными deb пакетами и попыток настроить её под себя я понял что ничего хорошего из этого не выйдет.

Первое что мне попалось в глаза это стандартный апплет погоды, который показывал белеберду.
А ведь я знаю, что на нашем местном университете установлены датчики температуры и текущая погода отображается на главной странице в виде картинки с замысловатым адресом
http://ito.osu.ru/termometr/curr_temp_graph.php?color=66,112,171&bgcolor=255,255,255
Я не знаю откуда скрипт берет данные, но на его выходе получается GIF с установленным цветом шрифта и фона.
Вот так и появилась идея написать собственный апплет.

Сказано, сделано!

Поковыряв гугл я выяснил что для написания апплетов в gnome используется библиотека gtk, а педевикия недвусмысленно вывела меня на страницу русского туториала.
Лениво пролистав его я попробовал скомпилировать первый Hell World
Для этого мне пришлось выкачать метров 40 библиотек и еще приблизительно метров 30 заняло NetBeans IDE (gedit, конечно рулит, но я уже как-то привык к автоформатированию и подсказанькам)

sudo apt-get install libgtk2.0-dev
Скомпилировав первую программу незамысловатой коммандой:
gcc test.c -o test `pkg-config --cflags --libs gtk+-2.0`
я радостный лег спать, ибо было уже 4 часа ночи (если не пять).

Сегодня, как только выдалась свободная минутка я сел за ноут :)
Идея была такова:

  1. Получить картинку и сохранить ее во временный файл
    Для этого нужно написать TCP клиента и отправить HTTP GET запрос на сервер ito.osu.ru, ну а потом отделить картинку от HTTP заголовка
  2. Отобразить ее в трее в виде иконки
    На этом этапе я основывался этой статьей

Вот что у меня получилось:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "eggtrayicon.h"

typedef unsigned int SOCKET;
#define SEND 0
#define RECV 1
int (*tcp_func)(SOCKET s, char* buf, int len, int flags);

#define FILE_IMG "/tmp/garik_temp.gif"

void destroy(GtkWidget *widget, gpointer data) {
    gtk_main_quit();
}

SOCKET tcp_client(char *host, unsigned short port) {
    struct sockaddr_in dest_addr;
    struct hostent *hst;
    SOCKET s;
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1;


    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(port);

    if ((dest_addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
        if (hst = gethostbyname(host))
            ((unsigned long *) & dest_addr.sin_addr)[0] = ((unsigned long **) hst->h_addr_list)[0][0];
        else {
            close(s);
            return -1;
        }
    }


    if (connect(s, (struct sockaddr *) & dest_addr, sizeof (dest_addr))) return -1;

    return s;

}


// функция гарантирует нам получение данных размером len

int tcp_rs(char type, SOCKET s, char *buf, int len, int flags) {
    int total = 0;
    int n;
    if (type == SEND) tcp_func = &send;
    else tcp_func = &recv;

    while (total < len) {
        n = tcp_func(s, buf + total, len - total, flags);

        if (n > 0) {
            total += n;
        } else if (n == 0) {
            //Reconnect(opt);
            close(s);
            return 0;
        } else {

            //n = WSAGetLastError();
            close(s);
            return (!errno + 1);


        }
    }

    return total;
}

char get_new_image() {
    SOCKET sock;
    char buff[1024];
    unsigned char search[] = {0x0D, 0x0A, 0x0D, 0x0A};
    unsigned char *p = NULL;
    int len;
    FILE *f;

    if (!(sock = tcp_client("ito.osu.ru", 80))) return 0;

    len = sprintf(buff, "GET /termometr/curr_temp_graph.php?color=255,255,255&bgcolor=0,0,0 HTTP/1.0\r\n\
Host: ito.osu.ru\r\n\
User-agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6\r\n\
Accept: image/*;q=0.9\r\n\
Referer: http://osu.ru/\r\n\
Connection: close\r\n\r\n");

    tcp_rs(SEND, sock, buff, len, 0);
    len = recv(sock, buff, sizeof (buff), MSG_PEEK); // балин хз
    tcp_rs(RECV, sock, buff, len, 0);

    close(sock);

    // самая волшебная часть )
    // зная размер пакета мы находим окончание заголовка \r\n\r\n =  0x0D, 0x0A, 0x0D, 0x0A
    p = memmem(buff, len, search, sizeof (search));
    if (p != NULL) {
        p += 4;
        len -= (int) p - (int) buff; // вычитаем заголовок из общей длины

        f = fopen(FILE_IMG, "w");
        fwrite(p, 1, len, f);
        fclose(f);

        return 1;
    }

    return 0;


}

GtkWidget *image, *event_box;
static EggTrayIcon *docklet = NULL;

void replace_img(char r) {
    if (r) gtk_container_remove(GTK_CONTAINER(event_box), image);
    get_new_image();
    image = gtk_image_new_from_file(FILE_IMG);
    gtk_container_add(GTK_CONTAINER(event_box), image);
    gtk_widget_show(GTK_WIDGET(image));
}

int main(int argc, char** argv) {

    gtk_init(&argc, &argv);
    docklet = egg_tray_icon_new("Rigidus");
    event_box = gtk_event_box_new();
    gtk_container_add(GTK_CONTAINER(docklet), event_box);

    if (!gtk_check_version(2, 4, 0)) {
        g_object_set(G_OBJECT(event_box), "visible-window", FALSE, NULL);
    }

    g_signal_connect(G_OBJECT(event_box), "button-press-event", G_CALLBACK(destroy), NULL);

    replace_img(0);

    gtk_widget_show_all(GTK_WIDGET(docklet));
    gtk_main();
    return (EXIT_SUCCESS);
}

Для того чтобы все это скомпилировать я написал небольшой скрипт

#! /bin/sh
gcc `pkg-config --cflags --libs gtk+-2.0` -c main.c
gcc `pkg-config --cflags --libs gtk+-2.0` -c eggtrayicon.c
gcc `pkg-config --cflags --libs gtk+-2.0` main.o eggtrayicon.o -o main

Вот и все! Оказывается в этом нет ничего сложного. Пока мой апплет умеет показываться в трее и вырубаться по нажатию на нем, но я уже намудрил поток который обновляет картинку раз в 30 секунд (жаль только моргает при прорисовке).
Еще бы найти, как сделать картинку прямиком из буфера да еще с прозрачным фоном... Найти настройки gnome где хранятся системные цвета окошек, написать макросы для преобразования цветов 0x00ffffff в формат 255,255,255...

Но я думаю это того не стоит хех

blog comments powered by Disqus
сюда туда