Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(882)

Side by Side Diff: chrome/browser/ui/libgtk2ui/app_indicator_icon.cc

Issue 716253002: Make system tray icons not show up as blurry on KDE (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@system_tray_proper
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/ui/libgtk2ui/app_indicator_icon.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon.h" 5 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon.h"
6 6
7 #include <dlfcn.h> 7 #include <dlfcn.h>
8 #include <gtk/gtk.h> 8 #include <gtk/gtk.h>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/environment.h" 11 #include "base/environment.h"
12 #include "base/files/file_util.h" 12 #include "base/files/file_util.h"
13 #include "base/md5.h" 13 #include "base/md5.h"
14 #include "base/memory/ref_counted_memory.h" 14 #include "base/memory/ref_counted_memory.h"
15 #include "base/nix/xdg_util.h" 15 #include "base/nix/xdg_util.h"
16 #include "base/strings/stringprintf.h" 16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h" 17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/sequenced_worker_pool.h" 18 #include "base/threading/sequenced_worker_pool.h"
19 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h" 19 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon_menu.h"
20 #include "content/public/browser/browser_thread.h" 20 #include "content/public/browser/browser_thread.h"
21 #include "third_party/skia/include/core/SkBitmap.h"
22 #include "third_party/skia/include/core/SkCanvas.h"
21 #include "ui/base/models/menu_model.h" 23 #include "ui/base/models/menu_model.h"
24 #include "ui/gfx/codec/png_codec.h"
22 #include "ui/gfx/image/image.h" 25 #include "ui/gfx/image/image.h"
23 #include "ui/gfx/image/image_skia.h" 26 #include "ui/gfx/image/image_skia.h"
24 27
25 namespace { 28 namespace {
26 29
27 typedef enum { 30 typedef enum {
28 APP_INDICATOR_CATEGORY_APPLICATION_STATUS, 31 APP_INDICATOR_CATEGORY_APPLICATION_STATUS,
29 APP_INDICATOR_CATEGORY_COMMUNICATIONS, 32 APP_INDICATOR_CATEGORY_COMMUNICATIONS,
30 APP_INDICATOR_CATEGORY_SYSTEM_SERVICES, 33 APP_INDICATOR_CATEGORY_SYSTEM_SERVICES,
31 APP_INDICATOR_CATEGORY_HARDWARE, 34 APP_INDICATOR_CATEGORY_HARDWARE,
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
127 130
128 app_indicator_set_icon_full = 131 app_indicator_set_icon_full =
129 reinterpret_cast<app_indicator_set_icon_full_func>( 132 reinterpret_cast<app_indicator_set_icon_full_func>(
130 dlsym(indicator_lib, "app_indicator_set_icon_full")); 133 dlsym(indicator_lib, "app_indicator_set_icon_full"));
131 134
132 app_indicator_set_icon_theme_path = 135 app_indicator_set_icon_theme_path =
133 reinterpret_cast<app_indicator_set_icon_theme_path_func>( 136 reinterpret_cast<app_indicator_set_icon_theme_path_func>(
134 dlsym(indicator_lib, "app_indicator_set_icon_theme_path")); 137 dlsym(indicator_lib, "app_indicator_set_icon_theme_path"));
135 } 138 }
136 139
137 // Returns whether a temporary directory should be created for each app 140 // Writes |bitmap| to a file at |path|. Returns true if successful.
138 // indicator image. 141 bool WriteFile(const base::FilePath& path, const SkBitmap& bitmap) {
139 bool ShouldCreateTempDirectoryPerImage(bool using_kde4) { 142 std::vector<unsigned char> png_data;
140 // Create a new temporary directory for each image on Unity since using a 143 if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_data))
141 // single temporary directory seems to have issues when changing icons in 144 return false;
142 // quick succession. 145 int bytes_written = base::WriteFile(
143 return !using_kde4; 146 path, reinterpret_cast<char*>(&png_data[0]), png_data.size());
144 } 147 return (bytes_written == static_cast<int>(png_data.size()));
145
146 // Returns the subdirectory of |temp_dir| in which the app indicator image
147 // should be saved.
148 base::FilePath GetImageDirectoryPath(bool using_kde4,
149 const base::FilePath& temp_dir) {
150 // On KDE4, an image located in a directory ending with
151 // "icons/hicolor/16x16/apps" can be used as the app indicator image because
152 // "/usr/share/icons/hicolor/16x16/apps" exists.
153 return using_kde4 ?
154 temp_dir.AppendASCII("icons").AppendASCII("hicolor").AppendASCII("16x16").
155 AppendASCII("apps") :
156 temp_dir;
157 }
158
159 std::string GetImageFileNameForKDE4(
160 const scoped_refptr<base::RefCountedMemory>& png_data) {
161 // On KDE4, the name of the image file for each different looking bitmap must
162 // be unique. It must also be unique across runs of Chrome.
163 base::MD5Digest digest;
164 base::MD5Sum(png_data->front_as<char>(), png_data->size(), &digest);
165 return base::StringPrintf("chrome_app_indicator_%s.png",
166 base::MD5DigestToBase16(digest).c_str());
167 }
168
169 std::string GetImageFileNameForNonKDE4(int icon_change_count,
170 const std::string& id) {
171 return base::StringPrintf("%s_%d.png", id.c_str(), icon_change_count);
172 }
173
174 // Returns the "icon theme path" given the file path of the app indicator image.
175 std::string GetIconThemePath(bool using_kde4,
176 const base::FilePath& image_path) {
177 return using_kde4 ?
178 image_path.DirName().DirName().DirName().DirName().value() :
179 image_path.DirName().value();
180 }
181
182 base::FilePath CreateTempImageFile(bool using_kde4,
183 gfx::ImageSkia* image_ptr,
184 int icon_change_count,
185 std::string id,
186 const base::FilePath& previous_file_path) {
187 scoped_ptr<gfx::ImageSkia> image(image_ptr);
188
189 scoped_refptr<base::RefCountedMemory> png_data =
190 gfx::Image(*image.get()).As1xPNGBytes();
191 if (png_data->size() == 0) {
192 // If the bitmap could not be encoded to PNG format, skip it.
193 LOG(WARNING) << "Could not encode icon";
194 return base::FilePath();
195 }
196
197 base::FilePath new_file_path;
198 if (previous_file_path.empty() ||
199 ShouldCreateTempDirectoryPerImage(using_kde4)) {
200 base::FilePath tmp_dir;
201 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &tmp_dir))
202 return base::FilePath();
203 new_file_path = GetImageDirectoryPath(using_kde4, tmp_dir);
204 if (new_file_path != tmp_dir) {
205 if (!base::CreateDirectory(new_file_path))
206 return base::FilePath();
207 }
208 } else {
209 new_file_path = previous_file_path.DirName();
210 }
211
212 new_file_path = new_file_path.Append(using_kde4 ?
213 GetImageFileNameForKDE4(png_data) :
214 GetImageFileNameForNonKDE4(icon_change_count, id));
215
216 int bytes_written =
217 base::WriteFile(new_file_path,
218 png_data->front_as<char>(), png_data->size());
219
220 if (bytes_written != static_cast<int>(png_data->size()))
221 return base::FilePath();
222 return new_file_path;
223 } 148 }
224 149
225 void DeleteTempDirectory(const base::FilePath& dir_path) { 150 void DeleteTempDirectory(const base::FilePath& dir_path) {
226 if (dir_path.empty()) 151 if (dir_path.empty())
227 return; 152 return;
228 base::DeleteFile(dir_path, true); 153 base::DeleteFile(dir_path, true);
229 } 154 }
230 155
231 } // namespace 156 } // namespace
232 157
(...skipping 15 matching lines...) Expand all
248 EnsureMethodsLoaded(); 173 EnsureMethodsLoaded();
249 tool_tip_ = base::UTF16ToUTF8(tool_tip); 174 tool_tip_ = base::UTF16ToUTF8(tool_tip);
250 SetImage(image); 175 SetImage(image);
251 } 176 }
252 AppIndicatorIcon::~AppIndicatorIcon() { 177 AppIndicatorIcon::~AppIndicatorIcon() {
253 if (icon_) { 178 if (icon_) {
254 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE); 179 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE);
255 g_object_unref(icon_); 180 g_object_unref(icon_);
256 content::BrowserThread::GetBlockingPool()->PostTask( 181 content::BrowserThread::GetBlockingPool()->PostTask(
257 FROM_HERE, 182 FROM_HERE,
258 base::Bind(&DeleteTempDirectory, icon_file_path_.DirName())); 183 base::Bind(&DeleteTempDirectory, temp_dir_.DirName()));
259 } 184 }
260 } 185 }
261 186
262 // static 187 // static
263 bool AppIndicatorIcon::CouldOpen() { 188 bool AppIndicatorIcon::CouldOpen() {
264 EnsureMethodsLoaded(); 189 EnsureMethodsLoaded();
265 return g_opened; 190 return g_opened;
266 } 191 }
267 192
268 void AppIndicatorIcon::SetImage(const gfx::ImageSkia& image) { 193 void AppIndicatorIcon::SetImage(const gfx::ImageSkia& image) {
269 if (!g_opened) 194 if (!g_opened)
270 return; 195 return;
271 196
272 ++icon_change_count_; 197 ++icon_change_count_;
273 198
274 // We create a deep copy of the image since it may have been freed by the time 199 // Copy the bitmap because it may be freed by the time it's accessed in
275 // it's accessed in the other thread. 200 // another thread.
276 scoped_ptr<gfx::ImageSkia> safe_image(image.DeepCopy()); 201 SkBitmap safe_bitmap = *image.bitmap();
277 base::PostTaskAndReplyWithResult( 202
203 scoped_refptr<base::TaskRunner> task_runner =
278 content::BrowserThread::GetBlockingPool() 204 content::BrowserThread::GetBlockingPool()
279 ->GetTaskRunnerWithShutdownBehavior( 205 ->GetTaskRunnerWithShutdownBehavior(
280 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN).get(), 206 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
281 FROM_HERE, 207 if (using_kde4_) {
282 base::Bind(&CreateTempImageFile, 208 base::PostTaskAndReplyWithResult(
283 using_kde4_, 209 task_runner.get(), FROM_HERE,
284 safe_image.release(), 210 base::Bind(AppIndicatorIcon::WriteKDE4TempImageOnWorkerThread,
285 icon_change_count_, 211 safe_bitmap, temp_dir_),
286 id_, 212 base::Bind(&AppIndicatorIcon::SetImageFromFile,
287 icon_file_path_), 213 weak_factory_.GetWeakPtr()));
288 base::Bind(&AppIndicatorIcon::SetImageFromFile, 214 } else {
289 weak_factory_.GetWeakPtr())); 215 base::PostTaskAndReplyWithResult(
216 task_runner.get(), FROM_HERE,
217 base::Bind(AppIndicatorIcon::WriteUnityTempImageOnWorkerThread,
218 safe_bitmap, icon_change_count_, id_),
219 base::Bind(&AppIndicatorIcon::SetImageFromFile,
220 weak_factory_.GetWeakPtr()));
221 }
290 } 222 }
291 223
292 void AppIndicatorIcon::SetToolTip(const base::string16& tool_tip) { 224 void AppIndicatorIcon::SetToolTip(const base::string16& tool_tip) {
293 DCHECK(!tool_tip_.empty()); 225 DCHECK(!tool_tip_.empty());
294 tool_tip_ = base::UTF16ToUTF8(tool_tip); 226 tool_tip_ = base::UTF16ToUTF8(tool_tip);
295 UpdateClickActionReplacementMenuItem(); 227 UpdateClickActionReplacementMenuItem();
296 } 228 }
297 229
298 void AppIndicatorIcon::UpdatePlatformContextMenu(ui::MenuModel* model) { 230 void AppIndicatorIcon::UpdatePlatformContextMenu(ui::MenuModel* model) {
299 if (!g_opened) 231 if (!g_opened)
300 return; 232 return;
301 233
302 menu_model_ = model; 234 menu_model_ = model;
303 235
304 // The icon is created asynchronously so it might not exist when the menu is 236 // The icon is created asynchronously so it might not exist when the menu is
305 // set. 237 // set.
306 if (icon_) 238 if (icon_)
307 SetMenu(); 239 SetMenu();
308 } 240 }
309 241
310 void AppIndicatorIcon::RefreshPlatformContextMenu() { 242 void AppIndicatorIcon::RefreshPlatformContextMenu() {
311 menu_->Refresh(); 243 menu_->Refresh();
312 } 244 }
313 245
314 void AppIndicatorIcon::SetImageFromFile(const base::FilePath& icon_file_path) { 246 // static
247 AppIndicatorIcon::SetImageFromFileParams
248 AppIndicatorIcon::WriteKDE4TempImageOnWorkerThread(
249 const SkBitmap& bitmap,
250 const base::FilePath& existing_temp_dir) {
251 base::FilePath temp_dir = existing_temp_dir;
252 if (temp_dir.empty() &&
253 !base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_dir)) {
254 LOG(WARNING) << "Could not create temporary directory";
255 return SetImageFromFileParams();
256 }
257
258 base::FilePath icon_theme_path = temp_dir.AppendASCII("icons");
259
260 // On KDE4, an image located in a directory ending with
261 // "icons/hicolor/24x24/apps" can be used as the app indicator image because
262 // "/usr/share/icons/hicolor/24x24/apps" exists.
263 base::FilePath image_dir = icon_theme_path.AppendASCII("hicolor")
264 .AppendASCII("24x24")
265 .AppendASCII("apps");
266
267 if (!base::CreateDirectory(image_dir))
268 return SetImageFromFileParams();
269
270 // On KDE4, the name of the image file for each different looking bitmap must
271 // be unique. It must also be unique across runs of Chrome.
272 std::vector<unsigned char> bitmap_png_data;
273 if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &bitmap_png_data)) {
274 LOG(WARNING) << "Could not encode icon";
275 return SetImageFromFileParams();
276 }
277 base::MD5Digest digest;
278 base::MD5Sum(reinterpret_cast<char*>(&bitmap_png_data[0]),
279 bitmap_png_data.size(), &digest);
280 std::string icon_name = base::StringPrintf(
281 "chrome_app_indicator2_%s", base::MD5DigestToBase16(digest).c_str());
282
283 // If |bitmap| is not 24x24, KDE does some really ugly resizing. Pad |bitmap|
284 // with transparent pixels to make it 24x24.
285 const int kDesiredSize = 24;
286 SkBitmap scaled_bitmap;
287 scaled_bitmap.allocN32Pixels(kDesiredSize, kDesiredSize);
288 scaled_bitmap.eraseARGB(0, 0, 0, 0);
289 SkCanvas canvas(scaled_bitmap);
290 canvas.drawBitmap(bitmap, (kDesiredSize - bitmap.width()) / 2,
291 (kDesiredSize - bitmap.height()) / 2);
292
293 base::FilePath image_path = image_dir.Append(icon_name + ".png");
294 if (!WriteFile(image_path, scaled_bitmap))
295 return SetImageFromFileParams();
296
297 SetImageFromFileParams params;
298 params.parent_temp_dir = temp_dir;
299 params.icon_theme_path = icon_theme_path.value();
300 params.icon_name = icon_name;
301 return params;
302 }
303
304 // static
305 AppIndicatorIcon::SetImageFromFileParams
306 AppIndicatorIcon::WriteUnityTempImageOnWorkerThread(const SkBitmap& bitmap,
307 int icon_change_count,
308 const std::string& id) {
309 // Create a new temporary directory for each image on Unity since using a
310 // single temporary directory seems to have issues when changing icons in
311 // quick succession.
312 base::FilePath temp_dir;
313 if (!base::CreateNewTempDirectory(base::FilePath::StringType(), &temp_dir)) {
314 LOG(WARNING) << "Could not create temporary directory";
315 return SetImageFromFileParams();
316 }
317
318 std::string icon_name =
319 base::StringPrintf("%s_%d", id.c_str(), icon_change_count);
320 base::FilePath image_path = temp_dir.Append(icon_name + ".png");
321 SetImageFromFileParams params;
322 if (WriteFile(image_path, bitmap)) {
323 params.parent_temp_dir = temp_dir;
324 params.icon_theme_path = temp_dir.value();
325 params.icon_name = icon_name;
326 }
327 return params;
328 }
329
330 void AppIndicatorIcon::SetImageFromFile(const SetImageFromFileParams& params) {
315 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 331 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
316 if (icon_file_path.empty()) 332 if (params.icon_theme_path.empty())
317 return; 333 return;
318 334
319 base::FilePath old_path = icon_file_path_;
320 icon_file_path_ = icon_file_path;
321
322 std::string icon_name =
323 icon_file_path_.BaseName().RemoveExtension().value();
324 std::string icon_dir = GetIconThemePath(using_kde4_, icon_file_path);
325 if (!icon_) { 335 if (!icon_) {
326 icon_ = 336 icon_ =
327 app_indicator_new_with_path(id_.c_str(), 337 app_indicator_new_with_path(id_.c_str(),
328 icon_name.c_str(), 338 params.icon_name.c_str(),
329 APP_INDICATOR_CATEGORY_APPLICATION_STATUS, 339 APP_INDICATOR_CATEGORY_APPLICATION_STATUS,
330 icon_dir.c_str()); 340 params.icon_theme_path.c_str());
331 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_ACTIVE); 341 app_indicator_set_status(icon_, APP_INDICATOR_STATUS_ACTIVE);
332 SetMenu(); 342 SetMenu();
333 } else { 343 } else {
334 // Currently we are creating a new temp directory every time the icon is 344 app_indicator_set_icon_theme_path(icon_, params.icon_theme_path.c_str());
335 // set. So we need to set the directory each time. 345 app_indicator_set_icon_full(icon_, params.icon_name.c_str(), "icon");
336 app_indicator_set_icon_theme_path(icon_, icon_dir.c_str()); 346 }
337 app_indicator_set_icon_full(icon_, icon_name.c_str(), "icon");
338 347
339 if (ShouldCreateTempDirectoryPerImage(using_kde4_)) { 348 if (temp_dir_ != params.parent_temp_dir) {
340 // Delete previous icon directory. 349 content::BrowserThread::GetBlockingPool()->PostTask(
341 content::BrowserThread::GetBlockingPool()->PostTask( 350 FROM_HERE,
342 FROM_HERE, 351 base::Bind(&DeleteTempDirectory, temp_dir_));
343 base::Bind(&DeleteTempDirectory, old_path.DirName())); 352 temp_dir_ = params.parent_temp_dir;
344 }
345 } 353 }
346 } 354 }
347 355
348 void AppIndicatorIcon::SetMenu() { 356 void AppIndicatorIcon::SetMenu() {
349 menu_.reset(new AppIndicatorIconMenu(menu_model_)); 357 menu_.reset(new AppIndicatorIconMenu(menu_model_));
350 UpdateClickActionReplacementMenuItem(); 358 UpdateClickActionReplacementMenuItem();
351 app_indicator_set_menu(icon_, menu_->GetGtkMenu()); 359 app_indicator_set_menu(icon_, menu_->GetGtkMenu());
352 } 360 }
353 361
354 void AppIndicatorIcon::UpdateClickActionReplacementMenuItem() { 362 void AppIndicatorIcon::UpdateClickActionReplacementMenuItem() {
(...skipping 10 matching lines...) Expand all
365 base::Bind(&AppIndicatorIcon::OnClickActionReplacementMenuItemActivated, 373 base::Bind(&AppIndicatorIcon::OnClickActionReplacementMenuItemActivated,
366 base::Unretained(this))); 374 base::Unretained(this)));
367 } 375 }
368 376
369 void AppIndicatorIcon::OnClickActionReplacementMenuItemActivated() { 377 void AppIndicatorIcon::OnClickActionReplacementMenuItemActivated() {
370 if (delegate()) 378 if (delegate())
371 delegate()->OnClick(); 379 delegate()->OnClick();
372 } 380 }
373 381
374 } // namespace libgtk2ui 382 } // namespace libgtk2ui
OLDNEW
« no previous file with comments | « chrome/browser/ui/libgtk2ui/app_indicator_icon.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698