diff -Nru unity-7.2.0+14.04.20140403/debian/changelog unity-7.2.0+14.04.20140404/debian/changelog --- unity-7.2.0+14.04.20140403/debian/changelog 2014-04-04 21:48:16.000000000 +0000 +++ unity-7.2.0+14.04.20140404/debian/changelog 2014-04-04 21:48:16.000000000 +0000 @@ -1,3 +1,18 @@ +unity (7.2.0+14.04.20140404-0ubuntu1) trusty; urgency=low + + [ Chris Townsend ] + * Fix issue where windows that are in Show Desktop mode are selected + for spread have a blank preview. This fix will Fade Out the window + for Spread and Fade In the window again when Spread exits if that + window is not chosen. (LP: #1104236) + + [ Marco Trevisan (TreviƱo) ] + * ForceQuitDialog: add a new "sheet style" dialog that is shown when a + window hangs Listen to compiz toolkit events and prompt a dialog + that allows to force-quit an hanging application. (LP: #698031) + + -- Ubuntu daily release Fri, 04 Apr 2014 21:35:41 +0000 + unity (7.2.0+14.04.20140403-0ubuntu1) trusty; urgency=low [ Stephen M. Webb ] diff -Nru unity-7.2.0+14.04.20140403/decorations/CMakeLists.txt unity-7.2.0+14.04.20140404/decorations/CMakeLists.txt --- unity-7.2.0+14.04.20140403/decorations/CMakeLists.txt 2014-04-03 11:09:23.000000000 +0000 +++ unity-7.2.0+14.04.20140404/decorations/CMakeLists.txt 2014-04-04 21:35:32.000000000 +0000 @@ -33,6 +33,7 @@ DecorationsMenuLayout.cpp DecorationsMenuEntry.cpp DecorationsMenuDropdown.cpp + DecorationsForceQuitDialog.cpp DecorationsDataPool.cpp ) diff -Nru unity-7.2.0+14.04.20140403/decorations/DecoratedWindow.cpp unity-7.2.0+14.04.20140404/decorations/DecoratedWindow.cpp --- unity-7.2.0+14.04.20140403/decorations/DecoratedWindow.cpp 2014-04-03 11:09:23.000000000 +0000 +++ unity-7.2.0+14.04.20140404/decorations/DecoratedWindow.cpp 2014-04-04 21:35:32.000000000 +0000 @@ -19,6 +19,7 @@ #include #include "DecorationsPriv.h" +#include "DecorationsForceQuitDialog.h" #include "DecorationsEdgeBorders.h" #include "DecorationsGrabEdge.h" #include "DecorationsWindowButton.h" @@ -47,7 +48,7 @@ , frame_(0) , dirty_geo_(true) , monitor_(0) - , cv_(unity::Settings::Instance().em()) + , cv_(Settings::Instance().em()) { active.changed.connect([this] (bool active) { bg_textures_.clear(); @@ -709,6 +710,30 @@ } } +void Window::Impl::UpdateForceQuitDialogPosition() +{ + if (force_quit_) + force_quit_->UpdateDialogPosition(); +} + +void Window::Impl::ShowForceQuitDialog(bool show, Time time) +{ + if (show) + { + if (!force_quit_) + { + force_quit_ = std::make_shared(win_, time); + force_quit_->close_request.connect([this] { force_quit_.reset(); }); + } + + force_quit_->time = time; + } + else + { + force_quit_.reset(); + } +} + // Public APIs Window::Window(CompWindow* cwin) @@ -765,6 +790,7 @@ impl_->UpdateMonitor(); impl_->ComputeShadowQuads(); impl_->UpdateDecorationTextures(); + impl_->UpdateForceQuitDialogPosition(); impl_->dirty_geo_ = false; } diff -Nru unity-7.2.0+14.04.20140403/decorations/DecorationsForceQuitDialog.cpp unity-7.2.0+14.04.20140404/decorations/DecorationsForceQuitDialog.cpp --- unity-7.2.0+14.04.20140403/decorations/DecorationsForceQuitDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ unity-7.2.0+14.04.20140404/decorations/DecorationsForceQuitDialog.cpp 2014-04-04 21:35:32.000000000 +0000 @@ -0,0 +1,455 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2014 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * 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, see . + * + * Authored by: Marco Trevisan + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include "DecorationsForceQuitDialog.h" +#include "DecorationStyle.h" +#include "WindowManager.h" + +namespace unity +{ +namespace decoration +{ +namespace +{ +DECLARE_LOGGER(logger, "unity.decoration.forcequit.dialog"); + +const std::string CLOSE_BUTTON_INACTIVE_FILE = "sheet_style_close_focused"; +const std::string CLOSE_BUTTON_FOCUSED_FILE = "sheet_style_close_focused_prelight"; +const std::string CLOSE_BUTTON_ACTIVE_FILE = "sheet_style_close_focused_pressed"; + + +// Dialog +struct SheetStyleWindow +{ + GtkWindow parent_instance; +}; + +struct SheetStyleWindowClass +{ + GtkWindowClass parent_class; +}; + +G_DEFINE_TYPE(SheetStyleWindow, sheet_style_window, GTK_TYPE_WINDOW); +static void sheet_style_window_init(SheetStyleWindow*) {} +static void sheet_style_window_class_init(SheetStyleWindowClass*); + +// Box +struct SheetStyleDialog +{ + GtkBox parent_instance; +}; + +struct SheetStyleDialogClass +{ + GtkBoxClass parent_class; +}; + +G_DEFINE_TYPE(SheetStyleDialog, sheet_style_dialog, GTK_TYPE_BOX); +GtkWidget* sheet_style_dialog_new(ForceQuitDialog*, Window, long); +static void sheet_style_dialog_init(SheetStyleDialog*) {} +static void sheet_style_dialog_class_init(SheetStyleDialogClass*); + +// Close button +struct CloseButtonPrivate +{ + GtkImage* img; +}; + +struct CloseButton +{ + GtkButton parent_instance; + CloseButtonPrivate* priv; +}; + +struct CloseButtonClass +{ + GtkButtonClass parent_class; +}; + +#define CLOSE_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), close_button_get_type(), CloseButton)) + +G_DEFINE_TYPE(CloseButton, close_button, GTK_TYPE_BUTTON); +GtkWidget* close_button_new(); +static void close_button_init(CloseButton*); +static void close_button_class_init(CloseButtonClass*); + +// Window implementation +GtkWidget* sheet_style_window_new(ForceQuitDialog* main_dialog, Window parent_xid) +{ + auto* dpy = gdk_x11_get_default_xdisplay(); + auto* self = GTK_WINDOW(g_object_new(sheet_style_window_get_type(), nullptr)); + gtk_window_set_skip_taskbar_hint(self, TRUE); + gtk_window_set_skip_pager_hint(self, TRUE); + gtk_window_set_position(self, GTK_WIN_POS_CENTER_ON_PARENT); + gtk_window_set_type_hint(self, GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_window_set_decorated(self, FALSE); + gtk_window_set_resizable(self, FALSE); + gtk_window_set_urgency_hint(self, TRUE); + gtk_window_set_deletable(self, FALSE); + gtk_window_set_title(self, "Force Quit Dialog"); + + XClassHint parent_class = {nullptr, nullptr}; + XGetClassHint(dpy, parent_xid, &parent_class); + gtk_window_set_wmclass(self, parent_class.res_name, parent_class.res_class); + XFree(parent_class.res_class); + XFree(parent_class.res_name); + + Atom WM_PID = gdk_x11_get_xatom_by_name("_NET_WM_PID"); + Atom WM_CLIENT_MACHINE = gdk_x11_get_xatom_by_name("WM_CLIENT_MACHINE"); + + auto& wm = WindowManager::Default(); + auto parent_hostname = wm.GetStringProperty(parent_xid, WM_CLIENT_MACHINE); + long parent_pid = 0; + + char current_hostname[256]; + if (gethostname(current_hostname, sizeof(current_hostname)) > -1) + { + current_hostname[sizeof(current_hostname)-1] = '\0'; + + if (current_hostname == parent_hostname) + { + auto const& pid_list = wm.GetCardinalProperty(parent_xid, WM_PID); + + if (!pid_list.empty()) + parent_pid = pid_list.front(); + } + } + + auto const& deco_style = decoration::Style::Get(); + auto const& offset = deco_style->ShadowOffset(); + int max_offset = std::max(std::abs(offset.x), std::abs(offset.y)); + gtk_container_set_border_width(GTK_CONTAINER(self), deco_style->ActiveShadowRadius()+max_offset); + + auto* screen = gtk_window_get_screen(self); + gtk_widget_set_visual(GTK_WIDGET(self), gdk_screen_get_rgba_visual(screen)); + gtk_widget_realize(GTK_WIDGET(self)); + gtk_widget_override_background_color(GTK_WIDGET(self), GTK_STATE_FLAG_NORMAL, nullptr); + + gtk_container_add(GTK_CONTAINER(self), sheet_style_dialog_new(main_dialog, parent_xid, parent_pid)); + + gtk_window_set_modal(self, TRUE); + gtk_window_set_destroy_with_parent(self, TRUE); + + auto* gwindow = gtk_widget_get_window(GTK_WIDGET(self)); + gdk_window_set_functions(gwindow, GDK_FUNC_CLOSE); + gtk_widget_realize(GTK_WIDGET(self)); + + gdk_error_trap_push(); + auto xid = gdk_x11_window_get_xid(gwindow); + XSetTransientForHint(dpy, xid, parent_xid); + XSync(dpy, False); + + if (int error_code = gdk_error_trap_pop()) + { + gchar tmp[1024]; + XGetErrorText(dpy, error_code, tmp, sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = '\0'; + LOG_ERROR(logger) << "Impossible to reparent dialog: " << tmp; + } + + XChangeProperty(dpy, xid, WM_CLIENT_MACHINE, XA_STRING, 8, PropModeReplace, + (unsigned char *) parent_hostname.c_str(), parent_hostname.size()); + XChangeProperty(dpy, xid, WM_PID, XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &parent_pid, 1); + XSync(dpy, False); + + return GTK_WIDGET(self); +} + +static void sheet_style_window_class_init(SheetStyleWindowClass* klass) +{ + GTK_WIDGET_CLASS(klass)->delete_event = [] (GtkWidget* self, GdkEventAny*) { + // Don't destroy the window on delete event + return TRUE; + }; + + GTK_WIDGET_CLASS(klass)->draw = [] (GtkWidget* self, cairo_t* cr) { + GtkAllocation a; + gtk_widget_get_allocation(self, &a); + cairo_rectangle(cr, 0, 0, a.width, a.height); + + cairo_save(cr); + int border = gtk_container_get_border_width(GTK_CONTAINER(self)); + cairo_translate(cr, border, border); + gtk_container_forall(GTK_CONTAINER(self), [] (GtkWidget* child, gpointer cr) { + gtk_widget_draw(child, static_cast(cr)); + }, cr); + cairo_restore(cr); + + return TRUE; + }; + + GTK_WIDGET_CLASS(klass)->size_allocate = [] (GtkWidget* self, GtkAllocation* a) { + GTK_WIDGET_CLASS(sheet_style_window_parent_class)->size_allocate(self, a); + + int border = gtk_container_get_border_width(GTK_CONTAINER(self)); + cairo_rectangle_int_t rect = {border, border, a->width-border*2, a->height-border*2}; + + auto* region = cairo_region_create_rectangle(&rect); + gtk_widget_input_shape_combine_region(GTK_WIDGET(self), region); + cairo_region_destroy(region); + }; +} + +// Dialog content Implementation +void on_force_quit_clicked(GtkButton *button, gint64* kill_data) +{ + Display* dpy = gdk_x11_get_default_xdisplay(); + GtkWidget* top_level = gtk_widget_get_toplevel(GTK_WIDGET(button)); + Window parent_xid = kill_data[0]; + long parent_pid = kill_data[1]; + + gtk_widget_hide(top_level); + + gdk_error_trap_push(); + XSync(dpy, False); + + if (int error_code = gdk_error_trap_pop()) + { + gchar tmp[1024]; + XGetErrorText(dpy, error_code, tmp, sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = '\0'; + LOG_ERROR(logger) << "Impossible to kill window " << parent_xid << ": " << tmp; + } + + XKillClient(dpy, parent_xid); + XSync(dpy, False); + + if (parent_pid > 0) + kill(parent_pid, 9); +} + +void on_wait_clicked(GtkButton *button, ForceQuitDialog* main_dialog) +{ + main_dialog->close_request.emit(); +} + +GtkWidget* sheet_style_dialog_new(ForceQuitDialog* main_dialog, Window parent_xid, long parent_pid) +{ + auto* self = GTK_WIDGET(g_object_new(sheet_style_dialog_get_type(), nullptr)); + gtk_orientable_set_orientation(GTK_ORIENTABLE(self), GTK_ORIENTATION_VERTICAL); + + glib::Object style(gtk_css_provider_new()); + + auto const& deco_style = decoration::Style::Get(); + auto const& radius = deco_style->CornerRadius(); + auto const& offset = deco_style->ShadowOffset(); + auto const& color = deco_style->ActiveShadowColor(); + int decoration_radius = std::max({radius.top, radius.left, radius.right, radius.bottom}); + + gtk_css_provider_load_from_data(style, (R"(SheetStyleDialog { + background-color: #f7f6f5; + color: #4a4a4a; + border-radius: )"+std::to_string(decoration_radius)+R"(px; + box-shadow: )"+std::to_string(offset.x)+"px "+std::to_string(offset.y)+"px "+ + std::to_string(deco_style->ActiveShadowRadius())+"px "+ + "rgba("+std::to_string(int(color.red * 255.0))+", "+ + std::to_string(int(color.green * 255.0))+", "+ + std::to_string(int(color.blue * 255.0))+", "+ + std::to_string(int(color.alpha))+'.'+std::to_string(int(color.alpha*10000.0))+')'+R"(; + })").c_str(), -1, nullptr); + + auto* style_ctx = gtk_widget_get_style_context(self); + gtk_style_context_add_provider(style_ctx, glib::object_cast(style), GTK_STYLE_PROVIDER_PRIORITY_FALLBACK); + gtk_style_context_add_class(style_ctx, "unity-force-quit"); + + auto* main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); + gtk_container_set_border_width(GTK_CONTAINER(main_box), 4); + + auto *close_button = close_button_new(); + g_signal_connect(close_button, "clicked", G_CALLBACK(on_wait_clicked), main_dialog); + gtk_box_pack_start(GTK_BOX(main_box), close_button, TRUE, TRUE, 0); + + auto* content_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); + gtk_container_set_border_width(GTK_CONTAINER(content_box), 10); + gtk_widget_set_margin_left(content_box, 20); + gtk_widget_set_margin_right(content_box, 20); + gtk_container_add(GTK_CONTAINER(main_box), content_box); + + auto* title = gtk_label_new(_("This window is not responding")); + auto* font_desc = pango_font_description_from_string("Ubuntu 17"); + gtk_widget_override_font(title, font_desc); + pango_font_description_free(font_desc); + gtk_widget_set_halign(title, GTK_ALIGN_START); + gtk_box_pack_start(GTK_BOX(content_box), title, FALSE, FALSE, 0); + + auto* subtitle = gtk_label_new(_("Do you want to force the application to exit, or wait for it to respond?")); + gtk_widget_set_halign(title, GTK_ALIGN_START); + gtk_box_pack_start(GTK_BOX(content_box), subtitle, FALSE, FALSE, 0); + + auto* buttons_box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); + gtk_box_set_spacing(GTK_BOX(buttons_box), 15); + gtk_container_set_border_width(GTK_CONTAINER(buttons_box), 5); + gtk_button_box_set_layout(GTK_BUTTON_BOX(buttons_box), GTK_BUTTONBOX_END); + + auto* wait_button = gtk_button_new_with_mnemonic(_("_Wait")); + gtk_container_add(GTK_CONTAINER(buttons_box), wait_button); + g_signal_connect(wait_button, "clicked", G_CALLBACK(on_wait_clicked), main_dialog); + + auto *kill_data = g_new(gint64, 2); + kill_data[0] = parent_xid; + kill_data[1] = parent_pid; + auto* quit_button = gtk_button_new_with_mnemonic(_("_Force Quit")); + gtk_container_add(GTK_CONTAINER(buttons_box), quit_button); + g_signal_connect_data(quit_button, "clicked", G_CALLBACK(on_force_quit_clicked), + kill_data, [] (gpointer data, GClosure*) { g_free(data); }, + static_cast(0)); + + auto* buttons_aligment = gtk_alignment_new(1, 1, 0, 0); + gtk_alignment_set_padding(GTK_ALIGNMENT(buttons_aligment), 20, 0, 0, 0); + gtk_container_add(GTK_CONTAINER(buttons_aligment), buttons_box); + gtk_container_add(GTK_CONTAINER(content_box), buttons_aligment); + + gtk_container_add(GTK_CONTAINER(self), main_box); + + return self; +} + +static void sheet_style_dialog_class_init(SheetStyleDialogClass* klass) +{ + GTK_WIDGET_CLASS(klass)->draw = [] (GtkWidget* self, cairo_t* cr) { + GtkAllocation a; + gtk_widget_get_allocation(self, &a); + gtk_render_background(gtk_widget_get_style_context(self), cr, 0, 0, a.width, a.height); + return GTK_WIDGET_CLASS(sheet_style_dialog_parent_class)->draw(self, cr); + }; +} + +// Close button +GtkWidget* close_button_new() +{ + auto* self = GTK_WIDGET(g_object_new(close_button_get_type(), nullptr)); + gtk_button_set_relief(GTK_BUTTON(self), GTK_RELIEF_NONE); + gtk_button_set_focus_on_click(GTK_BUTTON(self), FALSE); + gtk_widget_set_can_focus(self, FALSE); + gtk_widget_set_halign(self, GTK_ALIGN_START); + + auto const& file = decoration::Style::Get()->ThemedFilePath(CLOSE_BUTTON_INACTIVE_FILE, {PKGDATADIR"/"}); + auto* img = gtk_image_new_from_file(file.c_str()); + gtk_container_add(GTK_CONTAINER(self), img); + CLOSE_BUTTON(self)->priv->img = GTK_IMAGE(img); + + glib::Object style(gtk_css_provider_new()); + gtk_css_provider_load_from_data(style, R"( + * {padding: 0px 0px 0px 0px; border: 0px; } + )", -1, nullptr); + + auto* style_ctx = gtk_widget_get_style_context(self); + gtk_style_context_add_provider(style_ctx, glib::object_cast(style), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + return self; +} + +static void close_button_init(CloseButton* self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, close_button_get_type(), CloseButtonPrivate); +} + +static void close_button_class_init(CloseButtonClass* klass) +{ + GTK_WIDGET_CLASS(klass)->draw = [] (GtkWidget* self, cairo_t* cr) { + gtk_widget_draw(GTK_WIDGET(CLOSE_BUTTON(self)->priv->img), cr); + return TRUE; + }; + + GTK_WIDGET_CLASS(klass)->state_flags_changed = [] (GtkWidget* self, GtkStateFlags prev_state) { + auto* img = CLOSE_BUTTON(self)->priv->img; + if (!img) return; + + auto new_flags = gtk_widget_get_state_flags(self); + auto const& deco_style = decoration::Style::Get(); + auto file = deco_style->ThemedFilePath(CLOSE_BUTTON_INACTIVE_FILE, {PKGDATADIR"/"}); + + if (((new_flags & GTK_STATE_FLAG_PRELIGHT) && !gtk_widget_get_can_focus(self)) || + (new_flags & GTK_STATE_FLAG_FOCUSED)) + { + auto const& basename = (new_flags & GTK_STATE_FLAG_ACTIVE) ? CLOSE_BUTTON_ACTIVE_FILE : CLOSE_BUTTON_FOCUSED_FILE; + file = deco_style->ThemedFilePath(basename, {PKGDATADIR"/"}); + } + + gtk_image_set_from_file(img, file.c_str()); + + return GTK_WIDGET_CLASS(close_button_parent_class)->state_flags_changed(self, prev_state); + }; + + G_OBJECT_CLASS(klass)->finalize = [] (GObject* self) { + CLOSE_BUTTON(self)->priv->img = nullptr; + return G_OBJECT_CLASS(close_button_parent_class)->finalize(self); + }; +} + +} + +struct ForceQuitDialog::Impl : sigc::trackable +{ + Impl(ForceQuitDialog* parent, CompWindow* win) + : parent_(parent) + , win_(win) + , dialog_(sheet_style_window_new(parent, win_->id())) + { + parent_->time.changed.connect(sigc::mem_fun(this, &Impl::UpdateWindowTime)); + UpdateWindowTime(parent_->time()); + } + + ~Impl() + { + gtk_widget_destroy(dialog_); + } + + void UpdateWindowTime(Time time) + { + gdk_x11_window_set_user_time(gtk_widget_get_window(dialog_), time); + gtk_widget_show_all(dialog_); + } + + void UpdateDialogPosition() + { + auto const& win_geo = win_->inputRect(); + nux::Size walloc(gtk_widget_get_allocated_width(dialog_), gtk_widget_get_allocated_height(dialog_)); + gtk_window_move(GTK_WINDOW(dialog_), win_geo.centerX() - walloc.width/2, win_geo.centerY() - walloc.height/2); + } + + ForceQuitDialog* parent_; + CompWindow* win_; + GtkWidget* dialog_; +}; + +ForceQuitDialog::ForceQuitDialog(CompWindow* win, Time tm) + : time(tm) + , impl_(new Impl(this, win)) +{} + +ForceQuitDialog::~ForceQuitDialog() +{} + +void ForceQuitDialog::UpdateDialogPosition() +{ + impl_->UpdateDialogPosition(); +} + +} // decoration namespace +} // unity namespace diff -Nru unity-7.2.0+14.04.20140403/decorations/DecorationsForceQuitDialog.h unity-7.2.0+14.04.20140404/decorations/DecorationsForceQuitDialog.h --- unity-7.2.0+14.04.20140403/decorations/DecorationsForceQuitDialog.h 1970-01-01 00:00:00.000000000 +0000 +++ unity-7.2.0+14.04.20140404/decorations/DecorationsForceQuitDialog.h 2014-04-04 21:35:32.000000000 +0000 @@ -0,0 +1,53 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2014 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * 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, see . + * + * Authored by: Marco Trevisan + */ + +#ifndef __UNITY_DECORATIONS_FORCE_QUIT_DIALOG__ +#define __UNITY_DECORATIONS_FORCE_QUIT_DIALOG__ + +#include +#include "CompizUtils.h" + +namespace unity +{ +namespace decoration +{ + +class ForceQuitDialog +{ +public: + typedef std::shared_ptr Ptr; + + ForceQuitDialog(CompWindow*, Time); + ~ForceQuitDialog(); + + nux::Property