Skip to content

Commit

Permalink
Refactored to use Gtk composite templates
Browse files Browse the repository at this point in the history
  • Loading branch information
phw committed Dec 22, 2015
1 parent b675f13 commit 4d42c36
Show file tree
Hide file tree
Showing 5 changed files with 334 additions and 280 deletions.
27 changes: 17 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,35 @@ include(UseVala)
include(GResource)

# Check dependencies
find_package(Vala REQUIRED)
find_package(Vala "0.22" REQUIRED)

find_package(PkgConfig)
pkg_check_modules(GTK REQUIRED gtk+-3.0)
pkg_check_modules(GTK REQUIRED gtk+-3.0>=3.10)
add_definitions(${GTK_CFLAGS} ${GTK_CFLAGS_OTHER})
link_libraries(${GTK_LIBRARIES})
link_directories(${GTK_LIBRARY_DIRS})

pkg_check_modules(REQUIRED cairo gmodule-2.0)
pkg_check_modules(GLIB2 REQUIRED glib-2.0>=2.38)
pkg_check_modules(CAIRO REQUIRED cairo)
pkg_check_modules(GMODULE REQUIRED gmodule-2.0)

# compile glib resource files to c code
GLIB_COMPILE_RESOURCES(GLIB_RESOURCES
SOURCE
ui/${PROJECT_NAME}.gresource.xml
)

# Compile Vala to C
vala_precompile(VALA_C
src/main.vala src/screen-recorder.vala
src/main.vala
src/peek-application-window.vala
src/screen-recorder.vala
PACKAGES
gtk+-3.0
gmodule-2.0
)

# compile glib resource files to c code
GLIB_COMPILE_RESOURCES( GLIB_RESOURCES
SOURCE
ui/${PROJECT_NAME}.gresource.xml
OPTIONS
--target-glib=2.38
--gresources=../ui/${PROJECT_NAME}.gresource.xml
)

# Compile C code
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ A simple tool that allows you to record short animated Gifs from your screen.
## Requirements
### Runtime

* Gtk 3.?
* ffmpeg or byzanz?
* Gtk 3.10 or higher
* ffmpeg
* ImageMagick

### Development

Expand Down
268 changes: 4 additions & 264 deletions src/main.vala
Original file line number Diff line number Diff line change
Expand Up @@ -7,273 +7,13 @@ This software is licensed under the GNU General Public License
(version 3 or later). See the LICENSE file in this distribution.
*/

using Gtk;
using Cairo;

Window window;
Widget recording_view;
Button record_button;
Button stop_button;
Label size_indicator;
uint size_indicator_timeout = 0;
bool supports_alpha = true;
ScreenRecorder recorder = null;

public void on_application_window_screen_changed (Widget widget, Gdk.Screen oldScreen) {
var screen = widget.get_screen ();
var visual = screen.get_rgba_visual ();

if (visual == null) {
stderr.printf ("Screen does not support alpha channels!");
visual = screen.get_system_visual ();
supports_alpha = false;
}
else {
supports_alpha = true;
}

widget.set_visual (visual);
}

public bool on_application_window_configure_event (Gdk.EventConfigure event) {
if (recorder.is_recording) {
recorder.cancel ();
}

return false;
}

public bool on_recording_view_draw (Widget widget, Context ctx) {
if (supports_alpha) {
ctx.set_source_rgba (0.0, 0.0, 0.0, 0.0);
}
else {
ctx.set_source_rgb (0.0, 0.0, 0.0);
}

// Stance out the transparent inner part
ctx.set_operator (Operator.CLEAR);
ctx.paint ();
ctx.fill ();

// Set an input shape so that the recording view is not clickable
var window_region = create_region_from_widget (widget.get_toplevel());
var recording_viewRegion = create_region_from_widget (widget);
window_region.subtract (recording_viewRegion);
window.input_shape_combine_region (window_region);

return false;
}

public void on_recording_view_size_allocate (Widget widget, Rectangle rectangle) {
// Show the size
var size_label = new StringBuilder ();
var area = get_recording_area ();
size_label.printf ("%i x %i", area.width, area.height);
size_indicator.set_text (size_label.str);
size_indicator.show ();

if (size_indicator_timeout != 0) {
Source.remove (size_indicator_timeout);
}

if (!recorder.is_recording) {
size_indicator.opacity = 1.0;
size_indicator_timeout = Timeout.add (800, () => {
size_indicator.opacity = 0.0;
return false;
});
}
}

public void on_application_window_delete_event (string[] args) {
recorder.cancel ();
Gtk.main_quit ();
}

public void on_cancel_button_clicked (Button source) {
recorder.cancel ();
Gtk.main_quit ();
}

public void on_record_button_clicked (Button source) {
var area = get_recording_area ();
stdout.printf ("Recording area: %i, %i, %i, %i\n",
area.left, area.top, area.width, area.height);
recorder.record (area);
}

public void on_stop_button_clicked (Button source) {
recorder.stop ();
}

private void enter_recording_state () {
size_indicator.opacity = 0.0;
record_button.hide ();
stop_button.show ();
freeze_window_size ();
}

private void leave_recording_state () {
stop_button.hide ();
record_button.show ();
unfreeze_window_size ();
}

private void freeze_window_size () {
var width = window.get_allocated_width ();
var height = window.get_allocated_height ();
window.set_size_request (width, height);
window.resizable = false;
}

private void unfreeze_window_size () {
var width = window.get_allocated_width ();
var height = window.get_allocated_height ();
window.set_size_request (0, 0);
window.set_default_size (width, height);
window.resizable = true;
}

private Region create_region_from_widget (Widget widget) {
var rectangle = Cairo.RectangleInt () {
width = widget.get_allocated_width (),
height = widget.get_allocated_height ()
};

widget.translate_coordinates (widget.get_toplevel(), 0, 0, out rectangle.x, out rectangle.y);
var region = new Region.rectangle (rectangle);

return region;
}

private RecordingArea get_recording_area () {
var area = RecordingArea() {
width = recording_view.get_allocated_width (),
height = recording_view.get_allocated_height ()
};

// Get absoulte window coordinates
var recording_view_window = recording_view.get_window ();
recording_view_window.get_origin (out area.left, out area.top);

// FIXME: This is necessary for an exact position, not sure why.
area.top -= 1;

// Add relative widget coordinates
int relative_left, relative_top;
recording_view.translate_coordinates (recording_view.get_toplevel(), 0, 0,
out relative_left, out relative_top);
area.left += relative_left;
area.top += relative_top;

return area;
}

private void save_output (File in_file) {
var chooser = new FileChooserDialog (
"Select your favorite file", null, FileChooserAction.SAVE,
"_Cancel",
ResponseType.CANCEL,
"_Save",
ResponseType.ACCEPT);

var filter = new FileFilter ();
chooser.do_overwrite_confirmation = true;
chooser.filter = filter;
filter.add_mime_type ("image/gif");

var folder = get_video_folder ();
chooser.set_current_folder (folder);

var now = new DateTime.now_local ();
var default_name = now.format ("Peek %Y-%m-%d %H-%M.gif");
chooser.set_current_name (default_name);

if (chooser.run () == ResponseType.ACCEPT) {
var out_file = chooser.get_file ();

in_file.copy_async.begin (out_file, FileCopyFlags.OVERWRITE,
Priority.DEFAULT, null, null, (obj, res) => {
try {
bool copy_success = in_file.copy_async.end (res);
stdout.printf ("File saved %s: %s\n",
copy_success.to_string (),
out_file.get_uri ());

in_file.delete_async.begin (Priority.DEFAULT, null, (obj, res) => {
try {
bool delete_success = in_file.delete_async.end (res);
stdout.printf ("Temp file deleted: %s\n",
delete_success.to_string ());
} catch (Error e) {
stderr.printf ("Temp file delete error: %s\n", e.message);
}
});
}
catch (GLib.Error e) {
stderr.printf ("File save error: %s\n", e.message);
}
});
}

// Close the FileChooserDialog:
chooser.close ();
}

private string get_video_folder () {
string folder;
folder = GLib.Environment.get_user_special_dir (GLib.UserDirectory.VIDEOS);

if (folder == null) {
folder = GLib.Environment.get_user_special_dir (GLib.UserDirectory.PICTURES);
}

if (folder == null) {
folder = GLib.Environment.get_home_dir ();
}

return folder;
}

int main (string[] args) {
Gtk.init (ref args);

try {
recorder = new ScreenRecorder();
recorder.recording_started.connect (() => {
enter_recording_state ();
});
recorder.recording_finished.connect ((file) => {
leave_recording_state ();

if (file != null) {
save_output (file);
}
});
recorder.recording_aborted.connect ((status) => {
stderr.printf ("Recording stopped unexpectedly with return code %i\n", status);
leave_recording_state ();
});

var builder = new Builder ();
builder.add_from_resource ("/de/uploadedlobster/peek/peek.ui");
builder.connect_signals (null);

window = builder.get_object ("application_window") as Gtk.Window;
window.set_keep_above (true);

recording_view = builder.get_object ("recording_view") as Widget;
record_button = builder.get_object ("record_button") as Button;
stop_button = builder.get_object ("stop_button") as Button;
size_indicator = builder.get_object ("size_indicator") as Label;
Gtk.init (ref args);
var window = new PeekApplicationWindow ();
window.show_all ();

window.show_all ();
Gtk.main ();
} catch (Error e) {
stderr.printf ("Could not load UI: %s\n", e.message);
return 1;
}
Gtk.main ();

return 0;
}
Loading

0 comments on commit 4d42c36

Please sign in to comment.