Merge branch '10-save-restore-window-state' into 'master'

Resolve "Save & restore window state"

Closes #10

See merge request libreweb/browser!9
master
Melroy van den Berg 2021-03-28 23:48:58 +00:00
commit 02978f7565
10 changed files with 186 additions and 38 deletions

View File

@ -8,7 +8,7 @@ What would you do different; if you could **reinvent** The Internet in 21st cent
I was inspired by Douglas Engelbart, Tim Berners-Lee and Ted Nelson as well as projects like IPFS, Jekyll, ARPANET, and more. I was inspired by Douglas Engelbart, Tim Berners-Lee and Ted Nelson as well as projects like IPFS, Jekyll, ARPANET, and more.
*Note:* Project is still in Alpha phase! *Note:* Project is still in development!
## Download ## Download
@ -19,6 +19,10 @@ I was inspired by Douglas Engelbart, Tim Berners-Lee and Ted Nelson as well as p
![Browser Screenshot](./misc/browser_screenshot.png) ![Browser Screenshot](./misc/browser_screenshot.png)
![Browser Markdown Editor](./misc/browser_screenshot_2.png) ![Browser Markdown Editor](./misc/browser_screenshot_2.png)
## Documentation
Visit the [dedicated documentation site](https://docs.libreweb.org) for user documentation.
## Ideas/Features ## Ideas/Features
The current success criteria: The current success criteria:
@ -40,11 +44,11 @@ The current success criteria:
## Developers ## Developers
Decentralized Browser written in C++20 with C libraries. And using the [cmark-gfm](https://github.com/github/cmark-gfm) library, used for CommonMark (markdown) parsing. Decentralized Browser is written C++ together with some [libraries](/lib). It's using the [cmark-gfm](https://github.com/github/cmark-gfm) library for example, which is used for CommonMark (markdown) parsing.
Browser is using GTK 3 as UI library including Pango & Cairo for text drawing and manipulation. We're using markdown as the source-code of the content/site. No HTML and JavaScript anymore, content is king after all.
For now we will use markdown as the source of the site. No HTML and JavaScript anymore, content is king after all. LibreWeb Browser is using [Gnome GTK3](https://developer.gnome.org/gtk3/stable/) as UI framework.
### Development Environment ### Development Environment
@ -57,10 +61,10 @@ For the build you need at least:
* GCC 9 or higher (GCC 8 should also work, but not adviced. Package: `build-essential`) * GCC 9 or higher (GCC 8 should also work, but not adviced. Package: `build-essential`)
* CMake (Package: `cmake`) * CMake (Package: `cmake`)
* Ninja build system (Package: `ninja-build`) * Ninja build system (Package: `ninja-build`)
* GTK & Cairo & Pango (including C++ bindings): * GTK & Pango (including C++ bindings):
* Package: `libgtkmm-3.0-dev` under Debian based distros * Package: `libgtkmm-3.0-dev` under Debian based distros
### Documentation ### Developer Docs
See latest [Developer Docs](https://gitlab.melroy.org/libreweb/browser/-/jobs/artifacts/master/file/build/docs/html/index.html?job=doxygen). See latest [Developer Docs](https://gitlab.melroy.org/libreweb/browser/-/jobs/artifacts/master/file/build/docs/html/index.html?job=doxygen).

View File

@ -1,15 +1,14 @@
# Example: https://github.com/MariaDB/server/tree/10.5/cmake # Example: https://github.com/MariaDB/server/tree/10.5/cmake
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "LibreWeb Browser - Decentralized Web-Browser") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "LibreWeb Browser - Decentralized Web-Browser")
set(CPACK_PACKAGE_VENDOR "Melroy van den Berg") set(CPACK_PACKAGE_VENDOR "Melroy van den Berg")
set(CPACK_PACKAGE_CONTACT "Melroy van den Berg <info@libreweb.org>") set(CPACK_PACKAGE_CONTACT "Melroy van den Berg <info@libreweb.org>")
set(CPACK_PACKAGE_HOMEPAGE_URL "https://libreweb.org") set(CPACK_PACKAGE_HOMEPAGE_URL "https://libreweb.org")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/misc/package_desc.txt")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_TARGET}-${CPACK_PACKAGE_VERSION}") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_TARGET}-${CPACK_PACKAGE_VERSION}")
set(CPACK_DEBIAN_PACKAGE_SECTION "web") set(CPACK_DEBIAN_PACKAGE_SECTION "web")
set(CPACK_RPM_PACKAGE_GROUP "Applications/Internet") set(CPACK_RPM_PACKAGE_GROUP "Applications/Internet")
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-v${CPACK_PACKAGE_VERSION}") # Without '-Linux' suffix set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-v${CPACK_PACKAGE_VERSION}") # Without '-Linux' suffix
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND EXISTS "/etc/os-release") if (${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND EXISTS "/etc/os-release")

8
misc/package_desc.txt Normal file
View File

@ -0,0 +1,8 @@
LibreWeb Browser is a fully decentralized free software and open-source Web Browser.
Allowing users to easily browse and host new content on the decentralized web. Build on top of IPFS.
Decentralization comes with many benefits like resilient to censorship and no single point of failure.
I was inspired by Douglas Engelbart, Tim Berners-Lee and Ted Nelson as well as projects like IPFS, Jekyll and ARPANET.
Please read the documentation at https://docs.libreweb.org.

View File

@ -2,10 +2,13 @@
# By: Melroy van den Berg # By: Melroy van den Berg
# Description: Release production build + create Debian package file (.deb), # Description: Release production build + create Debian package file (.deb),
# RPM [Red Hat] Package Manager (.rpm) and compressed file (.tgz/.tar.gz) # RPM [Red Hat] Package Manager (.rpm) and compressed file (.tgz/.tar.gz)
#
# Installs into /usr folder (prefix)
rm -rf build_prod rm -rf build_prod
mkdir build_prod mkdir build_prod
cd build_prod cd build_prod
cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. cmake -GNinja -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=Release ..
ninja && ninja &&
echo "INFO: Building packages..."; echo "INFO: Building packages...";
cpack -G "TGZ;DEB;RPM" cpack -G "TGZ;DEB;RPM"

View File

@ -18,11 +18,11 @@ set(PROJECT_TARGET libreweb-browser)
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
# Find required dependencies
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(GTKMM gtkmm-3.0) pkg_check_modules(GTKMM REQUIRED gtkmm-3.0)
pkg_check_modules(CAIRO cairomm-1.0) pkg_check_modules(GLIB REQUIRED glib-2.0)
add_definitions(${GTKMM_CFLAGS_OTHER}) add_definitions(${GTKMM_CFLAGS_OTHER})
@ -39,8 +39,39 @@ if(NOT json_POPULATED)
add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL)
endif() endif()
# Generate Project version header file
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/project_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/project_config.h) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/project_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/project_config.h)
# Find the GLib path for schema installation
execute_process(
COMMAND
${PKG_CONFIG_EXECUTABLE}
glib-2.0
--variable prefix
OUTPUT_VARIABLE
_glib_prefix
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(GSETTINGS_DIR "${_glib_prefix}/share/glib-2.0/schemas/" CACHE INTERNAL "")
# Fetch path for schema compiler from pkg-config
execute_process(
COMMAND
${PKG_CONFIG_EXECUTABLE}
gio-2.0
--variable
glib_compile_schemas
OUTPUT_VARIABLE
_glib_compile_schemas
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Set glib schema compiler command
set(glib_schema_compiler ${_glib_compile_schemas} CACHE INTERNAL "")
set(SCHEMA_FILE ${CMAKE_CURRENT_SOURCE_DIR}/org.libreweb.browser.gschema.xml)
# Source code
set(HEADERS set(HEADERS
about.h about.h
file.h file.h
@ -51,7 +82,6 @@ set(HEADERS
draw.h draw.h
source-code-dialog.h source-code-dialog.h
) )
set(SOURCES set(SOURCES
main.cc main.cc
about.cc about.cc
@ -67,6 +97,21 @@ set(SOURCES
add_executable(${PROJECT_TARGET} ${SOURCES}) add_executable(${PROJECT_TARGET} ${SOURCES})
# Copy gschema.xml file to build directory
set(SCHEMA_DIR ${CMAKE_CURRENT_BINARY_DIR}/gsettings)
add_custom_command(
TARGET ${PROJECT_TARGET} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${SCHEMA_FILE}
${SCHEMA_DIR}/org.libreweb.browser.gschema.xml
)
add_custom_command(
TARGET ${PROJECT_TARGET} POST_BUILD
COMMAND ${glib_schema_compiler} ${SCHEMA_DIR}
COMMENT "Compiling schemas in folder: ${SCHEMA_DIR}"
)
# Add fallback for std filesystem in older GCC versions # Add fallback for std filesystem in older GCC versions
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.4) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.4)
@ -88,15 +133,18 @@ target_include_directories(${PROJECT_TARGET} PRIVATE
${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}
${CMAKE_EXTENSIONS_BINARY_DIR} ${CMAKE_EXTENSIONS_BINARY_DIR}
${GTKMM_INCLUDE_DIRS} ${GTKMM_INCLUDE_DIRS}
${CAIRO_INCLUDE_DIRS}
${PROJECT_SOURCE_DIR}/lib/ipfs-http-client/include ${PROJECT_SOURCE_DIR}/lib/ipfs-http-client/include
) )
target_link_directories(${PROJECT_TARGET} PRIVATE target_link_directories(${PROJECT_TARGET} PRIVATE
${GTKMM_LIBRARY_DIRS} ${GTKMM_LIBRARY_DIRS}
${CAIRO_LIBRARY_DIRS}
) )
target_link_libraries(${PROJECT_TARGET} PRIVATE LibCommonMarker LibCommonMarkerExtensions ipfs-http-client Threads::Threads ${CXX_FILESYSTEM_LIBRARIES} ${GTKMM_LIBRARIES} ${CAIRO_LIBRARIES} nlohmann_json::nlohmann_json) target_link_libraries(${PROJECT_TARGET} PRIVATE LibCommonMarker LibCommonMarkerExtensions ipfs-http-client Threads::Threads ${CXX_FILESYSTEM_LIBRARIES} ${GTKMM_LIBRARIES} nlohmann_json::nlohmann_json)
install(TARGETS ${PROJECT_TARGET} RUNTIME DESTINATION "bin" COMPONENT applications) # Install browser binary
install(TARGETS ${PROJECT_TARGET} RUNTIME DESTINATION bin)
# Install & compile GTK schema file
install(FILES ${SCHEMA_FILE} DESTINATION ${GSETTINGS_DIR})
install(CODE "execute_process (COMMAND ${glib_schema_compiler} ${GSETTINGS_DIR})")

View File

@ -127,12 +127,12 @@ bool IPFS::shouldKillRunningProcess()
std::string path = "/proc/" + std::to_string(pid) + "/exe"; std::string path = "/proc/" + std::to_string(pid) + "/exe";
if (readlink(path.c_str(), pathbuf, sizeof(pathbuf) - 1) > 0) if (readlink(path.c_str(), pathbuf, sizeof(pathbuf) - 1) > 0)
{ {
// TODO: Compare version or file path location char beginPath[] = "/usr/share/libreweb-browser";
char beginPath[28] = "/usr/share/libreweb-browser";
// If the begin path does not path (!= 0), return true, // If the begin path does not path (!= 0), return true,
// meaning the process will be killed. // meaning the process will be killed.
return (strncmp(pathbuf, beginPath, strlen(beginPath)) != 0); return (strncmp(pathbuf, beginPath, strlen(beginPath)) != 0);
} }
// TODO: Compare IPFS version as well, maybe?
} }
else else
{ {

View File

@ -1,5 +1,6 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "project_config.h"
#include "md-parser.h" #include "md-parser.h"
#include "menu.h" #include "menu.h"
#include "file.h" #include "file.h"
@ -10,14 +11,21 @@
#include <glibmm/miscutils.h> #include <glibmm/miscutils.h>
#include <glibmm/main.h> #include <glibmm/main.h>
#include <gdk-pixbuf/gdk-pixbuf.h> #include <gdk-pixbuf/gdk-pixbuf.h>
#include <glibmm/miscutils.h>
#include <cmark-gfm.h> #include <cmark-gfm.h>
#include <pthread.h> #include <pthread.h>
#include <iostream> #include <iostream>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
/**
* For info: NDEBUG variable be used for debugging purpose, example:
* #ifdef NDEBUG
* #endif
*/
MainWindow::MainWindow() MainWindow::MainWindow()
: accelGroup(Gtk::AccelGroup::create()), : m_accelGroup(Gtk::AccelGroup::create()),
m_menu(accelGroup), m_settings(),
m_menu(m_accelGroup),
m_draw_main(*this), m_draw_main(*this),
m_draw_secondary(*this), m_draw_secondary(*this),
m_vbox(Gtk::ORIENTATION_VERTICAL, 0), m_vbox(Gtk::ORIENTATION_VERTICAL, 0),
@ -41,7 +49,20 @@ MainWindow::MainWindow()
set_title(m_appName); set_title(m_appName);
set_default_size(1000, 800); set_default_size(1000, 800);
set_position(Gtk::WIN_POS_CENTER); set_position(Gtk::WIN_POS_CENTER);
add_accel_group(accelGroup); add_accel_group(m_accelGroup);
// Change schema directory when browser is not installed
if (!this->isInstalled())
{
std::string schemaDir = std::string(BINARY_DIR) + "/gsettings";
std::cout << "INFO: Use settings from: " << schemaDir << std::endl;
Glib::setenv("GSETTINGS_SCHEMA_DIR", schemaDir);
}
// Load schema settings file
m_settings = Gio::Settings::create("org.libreweb.browser");
set_default_size(m_settings->get_int("width"), m_settings->get_int("height"));
if (m_settings->get_boolean("maximized"))
this->maximize();
m_statusPopover.set_position(Gtk::POS_BOTTOM); m_statusPopover.set_position(Gtk::POS_BOTTOM);
m_statusPopover.set_size_request(200, 80); m_statusPopover.set_size_request(200, 80);
@ -53,7 +74,10 @@ MainWindow::MainWindow()
// Timeouts // Timeouts
this->statusTimerHandler = Glib::signal_timeout().connect(sigc::mem_fun(this, &MainWindow::update_connection_status), 3000); this->statusTimerHandler = Glib::signal_timeout().connect(sigc::mem_fun(this, &MainWindow::update_connection_status), 3000);
// Connect signals // Window signals
this->signal_delete_event().connect(sigc::mem_fun(this, &MainWindow::delete_window));
// Menu & toolbar signals
m_menu.new_doc.connect(sigc::mem_fun(this, &MainWindow::new_doc)); /*!< Menu item for new document */ m_menu.new_doc.connect(sigc::mem_fun(this, &MainWindow::new_doc)); /*!< Menu item for new document */
m_menu.open.connect(sigc::mem_fun(this, &MainWindow::open)); /*!< Menu item for opening existing document */ m_menu.open.connect(sigc::mem_fun(this, &MainWindow::open)); /*!< Menu item for opening existing document */
m_menu.save.connect(sigc::mem_fun(this, &MainWindow::save)); /*!< Menu item for save document */ m_menu.save.connect(sigc::mem_fun(this, &MainWindow::save)); /*!< Menu item for save document */
@ -372,13 +396,14 @@ MainWindow::MainWindow()
// timer will do the updates later // timer will do the updates later
this->update_connection_status(); this->update_connection_status();
#ifdef NDEBUG // Show homepage if debugging is disabled
// Show start page by default #ifdef NDEBUG
go_home(); go_home();
#else #else
// Load test.md file in debug std::cout << "INFO: Running as Debug mode, opening test.md." << std::endl;
doRequest("file://../../test.md", true); // Load test file when developing
#endif doRequest("file://../../test.md", true);
#endif
} }
/** /**
@ -404,6 +429,20 @@ void MainWindow::doRequest(const std::string &path, bool setAddressBar, bool isH
} }
} }
/**
* \brief Called when Window is closed
*/
bool MainWindow::delete_window(GdkEventAny *any_event __attribute__((unused)))
{
// Save the schema settings
m_settings->set_int("width", this->get_width());
m_settings->set_int("height", this->get_height());
m_settings->set_boolean("maximized", this->is_maximized());
// Fullscreen will be availible with gtkmm-4.0
//m_settings->set_boolean("fullscreen", this->is_fullscreen());
return false;
}
/** /**
* \brief Timeout slot: Update the IPFS connection status every x seconds * \brief Timeout slot: Update the IPFS connection status every x seconds
*/ */
@ -428,7 +467,7 @@ bool MainWindow::update_connection_status()
// And also update text // And also update text
m_statusLabel.set_text("IPFS Network Stats:\n\nConnected peers: " + std::to_string(nrPeers) + m_statusLabel.set_text("IPFS Network Stats:\n\nConnected peers: " + std::to_string(nrPeers) +
"\nRate in: " + in + " kB/s" + "\nRate in: " + in + " kB/s" +
"\nRate out: " + out + " kB/s"); "\nRate out: " + out + " kB/s");
} }
else else
{ {
@ -809,6 +848,25 @@ void MainWindow::refresh()
doRequest(); doRequest();
} }
/**
* \brief Determing if browser is installed from current binary path, at runtime
* \return true if the current running process is installed (to the installed prefix path)
*/
bool MainWindow::isInstalled()
{
char pathbuf[1024];
memset(pathbuf, 0, sizeof(pathbuf));
if (readlink("/proc/self/exe", pathbuf, sizeof(pathbuf) - 1) > 0)
{
// If current binary path starts with the install prefix, it's installed
return (strncmp(pathbuf, INSTALL_PREFIX, strlen(INSTALL_PREFIX)) == 0);
}
else
{
return true; // fallback; always installed
}
}
void MainWindow::enableEdit() void MainWindow::enableEdit()
{ {
// Inform the Draw class that we are creating a new document // Inform the Draw class that we are creating a new document

View File

@ -19,6 +19,7 @@
#include <gtkmm/searchbar.h> #include <gtkmm/searchbar.h>
#include <gtkmm/searchentry.h> #include <gtkmm/searchentry.h>
#include <gtkmm/paned.h> #include <gtkmm/paned.h>
#include <giomm/settings.h>
#include <thread> #include <thread>
/** /**
@ -33,6 +34,7 @@ public:
protected: protected:
// Signal handlers // Signal handlers
bool delete_window(GdkEventAny* any_event);
bool update_connection_status(); bool update_connection_status();
void cut(); void cut();
void copy(); void copy();
@ -60,7 +62,8 @@ protected:
void show_source_code_dialog(); void show_source_code_dialog();
void get_heading(); void get_heading();
Glib::RefPtr<Gtk::AccelGroup> accelGroup; /*!< Accelerator group, used for keyboard shortcut bindings */ Glib::RefPtr<Gtk::AccelGroup> m_accelGroup; /*!< Accelerator group, used for keyboard shortcut bindings */
Glib::RefPtr<Gio::Settings> m_settings; /*!< Settings to store our preferences, even during restarts */
// Child widgets // Child widgets
Menu m_menu; Menu m_menu;
@ -158,6 +161,7 @@ private:
sigc::connection statusTimerHandler; sigc::connection statusTimerHandler;
IPFS ipfs; IPFS ipfs;
bool isInstalled();
void enableEdit(); void enableEdit();
void disableEdit(); void disableEdit();
void postDoRequest(const std::string &path, bool setAddressBar, bool isHistoryRequest); void postDoRequest(const std::string &path, bool setAddressBar, bool isHistoryRequest);

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<schemalist>
<schema path="/org/libreweb/browser/" id="org.libreweb.browser">
<key name="width" type="i">
<default>1000</default>
<summary>Window width</summary>
</key>
<key name="height" type="i">
<default>800</default>
<summary>Window height</summary>
</key>
<key name="maximized" type="b">
<default>false</default>
<summary>Is window maximized</summary>
</key>
<key name="fullscreen" type="b">
<default>false</default>
<summary>Is window fullscreen</summary>
</key>
</schema>
</schemalist>

View File

@ -1,10 +1,13 @@
#ifndef INCLUDE_GUARD #ifndef PROJECT_CONFIG_H
#define INCLUDE_GUARD #define PROJECT_CONFIG_H
#define PROJECT_NAME "@PROJECT_NAME@" #define PROJECT_NAME "@PROJECT_NAME@"
#define PROJECT_VER "@PROJECT_VERSION@" #define PROJECT_VER "@PROJECT_VERSION@"
#define PROJECT_VER_MAJOR "@PROJECT_VERSION_MAJOR@" #define PROJECT_VER_MAJOR "@PROJECT_VERSION_MAJOR@"
#define PROJECT_VER_MINOR "@PROJECT_VERSION_MINOR@" #define PROJECT_VER_MINOR "@PROJECT_VERSION_MINOR@"
#define PTOJECT_VER_PATCH "@PROJECT_VERSION_PATCH@" #define PROJECT_VER_PATCH "@PROJECT_VERSION_PATCH@"
#endif // INCLUDE_GUARD #define INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
#define BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@"
#endif // PROJECT_CONFIG_H