eve/src/bin/main.c

702 lines
19 KiB
C

/*
* eve
*
* Copyright (C) 2009, Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
*
* License LGPL-3, see COPYING file at project folder.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <Elementary.h>
#ifndef ELM_LIB_QUICKLAUNCH
#include "prefs.h"
#include "favorite.h"
#include "private.h"
#include <Ecore_Getopt.h>
#include <Ecore_File.h>
#include <E_DBus.h>
#include <Ecore_X.h>
#include <stdlib.h>
#include "gettext.h"
int _log_domain = -1;
Fav *fav = NULL;
Hist *hist = NULL;
Prefs *prefs = NULL;
App app;
struct Cursor {
Evas_Object *object;
int layer;
int hot_x, hot_y;
};
struct Eve_DBus_Request_Name_Response {
E_DBus_Connection *conn;
const char *url;
};
static void
del_win(App *app, Evas_Object *win)
{
Browser_Window *win_data;
Eina_List *l;
EINA_LIST_FOREACH(app->windows, l, win_data) if (win_data->win == win)
break;
evas_object_del(win);
app->windows = eina_list_remove(app->windows, win_data);
free(win_data);
if (!app->windows)
elm_exit();
}
static void
on_win_del_req(void *data, Evas_Object *win, void *event_info __UNUSED__)
{
del_win(data, win);
}
void
window_mouse_enabled_set(Evas_Object *win, Eina_Bool setting)
{
Evas *e = evas_object_evas_get(win);
Ecore_Evas *ee = evas_data_attach_get(e);
static struct Cursor *default_cursor = NULL;
if (!default_cursor)
{
if (!(default_cursor = calloc(1, sizeof(*default_cursor)))) return;
ecore_evas_cursor_get(ee, &default_cursor->object, &default_cursor->layer,
&default_cursor->hot_x, &default_cursor->hot_y);
}
if (!setting)
{
Evas_Object *cursor = evas_object_rectangle_add(e);
evas_object_color_set(cursor, 0, 0, 0, 0);
evas_object_resize(cursor, 1, 1);
ecore_evas_object_cursor_set(ee, cursor, EVAS_LAYER_MIN, 0, 0);
}
else
ecore_evas_object_cursor_set(ee, default_cursor->object, default_cursor->layer,
default_cursor->hot_x, default_cursor->hot_y);
}
Eina_Bool
tab_add(Browser_Window *win, const char *url)
{
Evas_Object *chrome = chrome_add(win, url);
if (!chrome)
{
CRITICAL("Could not create chrome.");
goto error_chrome_create;
}
evas_object_size_hint_weight_set(chrome, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_show(chrome);
win->chromes = eina_list_append(win->chromes, chrome);
win->current_chrome = chrome;
win->current_view = evas_object_data_get(chrome, "view");
win->current_tab++;
return EINA_TRUE;
error_chrome_create:
evas_object_del(evas_object_data_get(chrome, "view"));
return EINA_FALSE;
}
Eina_Bool
tab_focus_chrome(Browser_Window *win, Evas_Object *chrome)
{
Eina_List *itr;
int n;
if (!chrome)
return EINA_FALSE;
for (n = 0, itr = win->chromes; itr->data != chrome; n++, itr = itr->next) ;
evas_object_hide(win->current_chrome);
win->current_chrome = chrome;
win->current_view = evas_object_data_get(chrome, "view");
win->current_tab = n;
evas_object_show(win->current_chrome);
evas_object_focus_set(win->current_view, EINA_TRUE);
elm_pager_content_promote(win->pager, win->current_chrome);
chrome_focused_notify(win->current_chrome);
return EINA_TRUE;
}
Eina_Bool
tab_focus_nth(Browser_Window *win, unsigned int n)
{
return tab_focus_chrome(win, eina_list_nth(win->chromes, n));
}
Eina_Bool
tab_focus_next(Browser_Window *win)
{
unsigned int n_tabs = eina_list_count(win->chromes);
if (win->current_tab > n_tabs)
return EINA_FALSE;
return tab_focus_nth(win, win->current_tab + 1);
}
Eina_Bool
tab_focus_prev(Browser_Window *win)
{
if (win->current_tab == 0)
return EINA_FALSE;
return tab_focus_nth(win, win->current_tab - 1);
}
Eina_Bool
tab_close_chrome(Browser_Window *win, Evas_Object *chrome)
{
Evas_Object *edje;
EINA_SAFETY_ON_TRUE_RETURN_VAL(!win, EINA_FALSE);
EINA_SAFETY_ON_TRUE_RETURN_VAL(!chrome, EINA_FALSE);
evas_object_del(chrome);
win->chromes = eina_list_remove(win->chromes, chrome);
if (!win->chromes)
{
del_win(win->app, win->win);
return EINA_TRUE;
}
if (win->current_chrome == chrome)
tab_focus_nth(win, 0);
edje = elm_layout_edje_get(win->current_chrome);
edje_object_signal_emit(edje, "hide,tab", "");
return EINA_TRUE;
}
Eina_Bool
tab_close_nth(Browser_Window *win, int n)
{
return tab_close_chrome(win, eina_list_nth(win->chromes, n));
}
Eina_Bool
tab_close_view(Browser_Window *win, Evas_Object *view)
{
return tab_close_chrome(win, evas_object_data_get(view, "chrome"));
}
static Browser_Window *
add_win(App *app, const char *url)
{
Browser_Window *win = malloc(sizeof(*win));
if (!win)
{
CRITICAL("Could not create window data.");
goto error_win_data;
}
win->app = app;
win->chromes = NULL;
win->current_chrome = NULL;
win->current_view = NULL;
win->current_tab = 0;
win->list_history = NULL;
win->list_history_titles = NULL;
win->win = elm_win_add(NULL, "eve", ELM_WIN_BASIC);
if (!win->win)
{
CRITICAL("Could not create window.");
goto error_win_create;
}
elm_win_title_set(win->win, PACKAGE_STRING);
elm_win_rotation_set(win->win, app->rotate);
elm_win_fullscreen_set(win->win, app->is_fullscreen);
window_mouse_enabled_set(win->win, prefs_enable_mouse_cursor_get(prefs));
win->bg = edje_object_add(evas_object_evas_get(win->win));
if (!win->bg)
{
CRITICAL("Could not create background.");
goto error_bg_create;
}
if (!edje_object_file_set(win->bg, PACKAGE_DATA_DIR "/default.edj", "bg"))
{
int err = edje_object_load_error_get(win->bg);
const char *msg = edje_load_error_str(err);
CRITICAL("Could not load background theme: %s", msg);
goto error_bg_theme_set;
}
evas_object_size_hint_weight_set(win->bg, EVAS_HINT_EXPAND,
EVAS_HINT_EXPAND);
elm_win_resize_object_add(win->win, win->bg);
evas_object_layer_set(win->bg, EVAS_LAYER_MIN);
evas_object_show(win->bg);
win->pager = elm_pager_add(win->win);
if (!win->pager)
{
CRITICAL("Could not create pager");
goto error_pager_create;
}
elm_object_style_set(win->pager, "ewebkit");
evas_object_size_hint_weight_set(win->pager, EVAS_HINT_EXPAND,
EVAS_HINT_EXPAND);
elm_win_resize_object_add(win->win, win->pager);
evas_object_show(win->pager);
if (!tab_add(win, url))
goto error_tab_add;
app->windows = eina_list_append(app->windows, win);
evas_object_smart_callback_add
(win->win, "delete-request", on_win_del_req, app);
evas_object_resize(win->win, 480, 800);
evas_object_show(win->win);
return win;
error_bg_theme_set:
evas_object_del(win->bg);
error_bg_create:
evas_object_del(win->win);
error_win_create:
free(win);
error_win_data:
error_tab_add:
error_pager_create:
return NULL;
}
/**
* Creates a new window, without any url to load, calling add_win().
*
* @return If a window was successfully created, it returns the correspondent view
* object.
*/
Evas_Object *
window_create(void)
{
Browser_Window *win = add_win(&app, NULL);
if (!win)
return NULL;
return win->current_view;
}
void
window_title_set(Browser_Window *win, const char *title)
{
char buf[4096];
EINA_SAFETY_ON_NULL_RETURN(win);
if (!title)
{
elm_win_title_set(win->win, PACKAGE_STRING);
return;
}
snprintf(buf, sizeof(buf), "%s - %s", title, PACKAGE_STRING);
elm_win_title_set(win->win, buf);
}
#define stringify(X) #X
static const Ecore_Getopt options = {
PACKAGE_NAME,
"%prog [options] [url]",
PACKAGE_VERSION "Revision:" stringify(VREV),
"(C) 2010 ProFUSION embedded systems",
"LGPL-3",
"WebKit-EFL demo browser for mobile systems with touchscreen.",
EINA_TRUE,
{
ECORE_GETOPT_STORE_DEF_BOOL('F', "fullscreen", "start in fullscreen.", 1),
ECORE_GETOPT_STORE_DEF_BOOL('P', "disable-plugins",
"disable plugins (flash, etc).", 1),
ECORE_GETOPT_STORE_DEF_BOOL('M', "disable-mouse",
"disable mouse (hide it).", 1),
ECORE_GETOPT_STORE_DEF_BOOL('T', "disable-touch-interface",
"disable touch interface handling of mouse events", 1),
ECORE_GETOPT_STORE_STR('U', "user-agent",
"user agent string to use. Special cases=iphone,safari,chrome,firefox,ie,ie9,ie8,ie7."),
ECORE_GETOPT_STORE_DEF_UINT('R', "rotate", "Screen Rotation in degrees", 0),
ECORE_GETOPT_VERSION('V', "version"),
ECORE_GETOPT_COPYRIGHT('C', "copyright"),
ECORE_GETOPT_LICENSE('L', "license"),
ECORE_GETOPT_HELP('h', "help"),
ECORE_GETOPT_SENTINEL
}
};
static DBusMessage *
_cb_dbus_open_url(E_DBus_Object *obj __UNUSED__, DBusMessage *msg)
{
DBusMessage *reply;
Browser_Window *win = eina_list_data_get(app.windows);
char *new_url;
dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &new_url, DBUS_TYPE_INVALID);
tab_add(win, new_url);
ecore_x_window_focus(elm_win_xwindow_get(win->win));
return dbus_message_new_method_return(msg);
}
static void
_cb_dbus_request_name(void *data, DBusMessage *msg __UNUSED__, DBusError *err)
{
struct Eve_DBus_Request_Name_Response *response = data;
DBusError new_err;
dbus_uint32_t ret;
if (dbus_error_is_set(err))
{
dbus_error_free(err);
goto cleanup;
}
dbus_error_init(&new_err);
dbus_message_get_args(msg, &new_err, DBUS_TYPE_UINT32, &ret, DBUS_TYPE_INVALID);
switch (ret) {
case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
{
E_DBus_Interface *iface = e_dbus_interface_new("mobi.profusion.eve");
E_DBus_Object *eve_dbus = e_dbus_object_add(response->conn, "/mobi/profusion/eve", NULL);
e_dbus_interface_method_add(iface, "open_url", "s", "", _cb_dbus_open_url);
e_dbus_object_interface_attach(eve_dbus, iface);
}
break;
case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
case DBUS_REQUEST_NAME_REPLY_EXISTS:
{
DBusMessage *open_url = dbus_message_new_method_call("mobi.profusion.eve",
"/mobi/profusion/eve",
"mobi.profusion.eve",
"open_url");
DBusMessageIter iter;
dbus_message_iter_init_append(open_url, &iter);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &response->url);
e_dbus_message_send(response->conn, open_url, NULL, -1, NULL);
dbus_message_unref(open_url);
}
exit(0);
}
cleanup:
free(response);
}
static void
state_save(void)
{
Browser_Window *win;
Eina_List *win_iter;
if (!prefs_restore_state_get(prefs)) return;
prefs_state_list_clear(prefs);
EINA_LIST_FOREACH(app.windows, win_iter, win)
{
Evas_Object *chrome;
Eina_List *chrome_iter;
EINA_LIST_FOREACH(win->chromes, chrome_iter, chrome)
{
Evas_Object *view = evas_object_data_get(chrome, "view");
prefs_state_add(prefs, prefs_opened_tab_new(ewk_view_uri_get(view)));
}
}
}
EAPI int
elm_main(int argc, char **argv)
{
int r = 0, args;
const char *home;
const char *url;
char path[PATH_MAX], *basename;
Eina_Bool quit_option = EINA_FALSE;
Eina_Bool disable_plugins = 0xff;
Eina_Bool disable_mouse_cursor = 0xff;
Eina_Bool disable_touch_interface = 0xff;
char *user_agent_option = NULL;
const char *user_agent_str;
E_DBus_Connection *conn;
size_t dirlen;
Ecore_Getopt_Value values[] = {
ECORE_GETOPT_VALUE_BOOL(app.is_fullscreen),
ECORE_GETOPT_VALUE_BOOL(disable_plugins),
ECORE_GETOPT_VALUE_BOOL(disable_mouse_cursor),
ECORE_GETOPT_VALUE_BOOL(disable_touch_interface),
ECORE_GETOPT_VALUE_STR(user_agent_option),
ECORE_GETOPT_VALUE_UINT(app.rotate),
ECORE_GETOPT_VALUE_BOOL(quit_option),
ECORE_GETOPT_VALUE_BOOL(quit_option),
ECORE_GETOPT_VALUE_BOOL(quit_option),
ECORE_GETOPT_VALUE_BOOL(quit_option),
ECORE_GETOPT_VALUE_NONE
};
#if ENABLE_NLS
setlocale(LC_ALL, "");
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
textdomain(GETTEXT_PACKAGE);
#endif
_log_domain = eina_log_domain_register("eve", NULL);
if (_log_domain < 0)
{
EINA_LOG_CRIT("could not create log domain 'eve'.");
return -1;
}
args = ecore_getopt_parse(&options, values, argc, argv);
if (args < 0)
{
ERR("Could not parse command line options.");
return -1;
}
if (quit_option)
{
DBG("Command lines option requires quit.");
return 0;
}
if (!user_agent_option ||
(user_agent_option && (strcasecmp(user_agent_option, "eve") == 0)))
user_agent_str = "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3 " PACKAGE_NAME "/" PACKAGE_VERSION;
else
{
/* http://www.useragentstring.com/ */
if (strcasecmp(user_agent_option, "iphone") == 0)
user_agent_str = "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3";
else if (strcasecmp(user_agent_option, "safari") == 0)
user_agent_str = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-US) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8";
else if (strcasecmp(user_agent_option, "chrome") == 0)
user_agent_str = "Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7";
else if (strcasecmp(user_agent_option, "firefox") == 0)
user_agent_str = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.2) Gecko/20121223 Firefox/3.8";
else if (strcasecmp(user_agent_option, "ie") == 0) /* last */
user_agent_str = "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)";
else if (strcasecmp(user_agent_option, "ie9") == 0)
user_agent_str = "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)";
else if (strcasecmp(user_agent_option, "ie8") == 0)
user_agent_str = "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322)";
else if (strcasecmp(user_agent_option, "ie7") == 0)
user_agent_str = "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)";
else
user_agent_str = user_agent_option;
}
elm_theme_extension_add(NULL, PACKAGE_DATA_DIR "/default.edj");
ewk_init();
favorite_init();
history_init();
preferences_init();
e_dbus_init();
home = getenv("HOME");
if (!home || !home[0])
{
CRITICAL("Could not get $HOME");
r = -1;
goto end;
}
dirlen = snprintf(path, sizeof(path), "%s/.config/eve", home);
if (dirlen >= sizeof(path))
{
ERR("Path is too long: %s/.config/eve", home);
r = -1;
goto end;
}
if (!ecore_file_mkpath(path))
{
ERR("Could not create %s", path);
r = -1;
goto end;
}
if (!ewk_settings_icon_database_path_set(path))
{
ERR("Could not set icon database path to %s", path);
r = -1;
goto end;
}
basename = path + dirlen;
basename[0] = '/';
basename++;
dirlen++;
eina_strlcpy(basename, "favorites.db", sizeof(path) - dirlen);
fav = fav_load(path);
if (!fav)
{
fav = fav_new(0);
fav_save(fav, path);
}
eina_strlcpy(basename, "history.db", sizeof(path) - dirlen);
hist = hist_load(path);
if (!hist)
{
hist = hist_new(0);
hist_save(hist, path);
}
eina_strlcpy(basename, "prefs.db", sizeof(path) - dirlen);
prefs = prefs_load(path);
if (!prefs)
{
Eina_Bool enable_mouse_cursor, enable_touch_interface, enable_plugins;
#define BOOL_OPT(opt) \
enable_##opt = ((disable_##opt == 0xff) ? EINA_TRUE : !disable_##opt)
BOOL_OPT(mouse_cursor);
BOOL_OPT(touch_interface);
BOOL_OPT(plugins);
#undef BOOL_OPT
prefs = prefs_new(enable_mouse_cursor, enable_touch_interface,
enable_plugins,
EINA_TRUE, user_agent_str, DEFAULT_URL, NULL,
EINA_FALSE, EINA_TRUE, EINA_FALSE,
EINA_TRUE, EINA_FALSE, NULL);
prefs_save(prefs, path);
}
#define BOOL_OPT(opt) \
if (disable_##opt != 0xff) \
{ \
Eina_Bool old = prefs_enable_##opt##_get(prefs); \
Eina_Bool cur = !disable_##opt; \
if (old != cur) \
{ \
INF("Changed preferences to "#opt"=%hhu", cur); \
prefs_enable_##opt##_set(prefs, cur); \
} \
}
BOOL_OPT(mouse_cursor);
BOOL_OPT(touch_interface);
BOOL_OPT(plugins);
#undef BOOL_OPT
if (user_agent_option)
{
const char *old = prefs_user_agent_get(prefs);
const char *cur = user_agent_str;
if (strcmp(old, cur) != 0)
{
INF("Changed preferences to user_agent=\"%s\"", cur);
prefs_user_agent_set(prefs, cur);
}
}
if (args < argc)
url = argv[args];
else
url = prefs_home_page_get(prefs);
conn = e_dbus_bus_get(DBUS_BUS_SESSION);
if (conn)
{
struct Eve_DBus_Request_Name_Response *response = calloc(1, sizeof(*response));
if (!response) goto end;
response->conn = conn;
response->url = url;
e_dbus_request_name(conn, "mobi.profusion.eve", 0, _cb_dbus_request_name, response);
}
if (prefs_restore_state_get(prefs) && prefs_state_count(prefs) > 0)
{
Eina_List *previous_state = prefs_state_list_get(prefs);
Eina_List *state_iter;
Prefs_Opened_Tab *tab = eina_list_data_get(previous_state);
Browser_Window *win;
if (!add_win(&app, prefs_opened_tab_address_get(tab)))
{
r = -1;
goto end;
}
win = eina_list_data_get(app.windows);
EINA_LIST_FOREACH(previous_state->next, state_iter, tab)
tab_add(win, prefs_opened_tab_address_get(tab));
}
else if (!add_win(&app, url))
{
r = -1;
goto end;
}
elm_run();
end:
state_save();
fav_save(fav, NULL);
fav_free(fav);
hist_save(hist, NULL);
hist_free(hist);
prefs_save(prefs, NULL);
prefs_free(prefs);
if (conn) e_dbus_connection_close(conn);
eina_log_domain_unregister(_log_domain);
_log_domain = -1;
elm_shutdown();
ewk_shutdown();
favorite_shutdown();
history_shutdown();
preferences_shutdown();
e_dbus_shutdown();
return r;
}
#endif
ELM_MAIN()