From 40a38a2f9b45bb73460145148bd4e4560e0d9672 Mon Sep 17 00:00:00 2001
From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
Date: Fri, 30 Dec 2011 23:32:35 -0600
Subject: Add initial support for ELF embedded icons in Konqueror This feature
 still needs quite a bit of work to add embedding support to other parts of
 TDE such as the minicli, as well as bringing konqueror behaviour more in line
 with the minicli previews, but it is a start at least!

---
 CMakeLists.txt         |  21 ++++++
 config.h.cmake         |   3 +
 kio/CMakeLists.txt     |   3 +-
 kio/kio/CMakeLists.txt |   1 +
 kio/kio/kfileitem.cpp  | 169 +++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 196 insertions(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6962394a0..86c2fcdda 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,6 +75,7 @@ OPTION( WITH_JASPER "Enable jasper (jpeg2k) support" OFF )
 OPTION( WITH_OPENEXR "Enable openexr support" OFF )
 OPTION( WITH_UTEMPTER "Use utempter for utmp management" OFF )
 OPTION( WITH_AVAHI "Enable AVAHI support" OFF )
+OPTION( WITH_ELFICON "Enable ELF embedded icon support" OFF )
 
 OPTION( WITH_ASPELL "Enable aspell support" OFF )
 OPTION( WITH_HSPELL "Enable hspell support" OFF )
@@ -715,6 +716,26 @@ if( WITH_AVAHI )
   endif( WITH_QT3 )
 endif( )
 
+
+##### check for libr ###########################
+# WARNING
+# before altering the LIBR_VERSION check,
+# please ensure that nothing changed in the
+# duplicated data definitions present in
+# kio/kio/kfileitem.cpp
+
+if( WITH_ELFICON )
+  pkg_search_module( LIBR libr )
+  if( NOT LIBR_FOUND )
+      message(FATAL_ERROR "\nelficon support was requested, but libr was not found on your system" )
+  endif( NOT LIBR_FOUND )
+  if( LIBR_VERSION != 0.6.0 )
+      message(FATAL_ERROR "\nelficon support was requested, but the libr version on your system may not be compatible with TDE" )
+  endif( LIBR_VERSION != 0.6.0 )
+  set( HAVE_ELFICON 1 )
+endif( )
+
+
 ##### check for aspell ##########################
 # we need ASPELL_DATADIR too
 
diff --git a/config.h.cmake b/config.h.cmake
index c52da286f..80b8d071c 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -110,6 +110,9 @@
 /* Define if you have OpenEXR */
 #cmakedefine HAVE_EXR 1
 
+/* Defined if you have elficon support. */
+#cmakedefine HAVE_ELFICON 1
+
 /* Define is posix_fadvise is supported */
 #undef HAVE_FADVISE
 
diff --git a/kio/CMakeLists.txt b/kio/CMakeLists.txt
index b18bfef33..73c2c9dba 100644
--- a/kio/CMakeLists.txt
+++ b/kio/CMakeLists.txt
@@ -30,6 +30,7 @@ include_directories(
 
 link_directories(
   ${TQT_LIBRARY_DIRS}
+  ${LIBR_LIBDIR}
 )
 
 
@@ -60,6 +61,6 @@ tde_add_library( ${target} SHARED
   SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dummy.cpp
   VERSION 4.2.0
   EMBED kssl-static kiocore-static ksycoca-static kbookmarks-static kfile-static
-  LINK tdeui-shared tdesu-shared kwalletclient-shared
+  LINK tdeui-shared tdesu-shared kwalletclient-shared ${LIBR_LIBRARIES}
   DESTINATION ${LIB_INSTALL_DIR}
 )
diff --git a/kio/kio/CMakeLists.txt b/kio/kio/CMakeLists.txt
index f9976a6d2..479814f15 100644
--- a/kio/kio/CMakeLists.txt
+++ b/kio/kio/CMakeLists.txt
@@ -24,6 +24,7 @@ include_directories(
   ${CMAKE_SOURCE_DIR}/kio
   ${CMAKE_SOURCE_DIR}/kio/kssl
   ${CMAKE_SOURCE_DIR}/interfaces
+  ${LIBR_INCLUDEDIR}
 )
 
 
diff --git a/kio/kio/kfileitem.cpp b/kio/kio/kfileitem.cpp
index 010832573..eaedf0a2d 100644
--- a/kio/kio/kfileitem.cpp
+++ b/kio/kio/kfileitem.cpp
@@ -19,6 +19,8 @@
 */
 // $Id$
 
+#include <config.h>
+
 #include <sys/time.h>
 #include <pwd.h>
 #include <grp.h>
@@ -46,6 +48,115 @@
 #include <kmimetype.h>
 #include <krun.h>
 
+#ifdef HAVE_ELFICON
+#include <alloca.h>
+#include <stdint.h>
+#include <cstdlib>
+
+extern "C" {
+  #include <libr-icons.h>
+
+  // BEGIN HACK
+  // libr does not export these structures and defines,
+  // but we need access to them to make the UI behave sanely
+  // Keep them in sync with libr and all should be OK
+
+  // Valid for libr version 0.6.0
+  // See libr detection code in ConfigureChecks.cmake
+
+  typedef uint32_t ID8;
+  typedef uint16_t ID4;
+  typedef struct {uint64_t p:48;} __attribute__((__packed__)) ID12;
+
+  typedef struct {
+    ID8  g1;
+    ID4  g2;
+    ID4  g3;
+    ID4  g4;
+    ID12 g5;
+  } __attribute__((__packed__)) UUID;
+
+  typedef struct {
+    char *name;
+    size_t offset;
+    size_t entry_size;
+    libr_icontype_t type;
+    unsigned int icon_size;
+  } iconentry;
+
+  typedef struct{
+    size_t size;
+    char *buffer;
+    iconentry entry;
+  } iconlist;
+
+  #define ICON_SECTION     ".icon"
+  // END HACK
+
+//   int get_iconlist(libr_file *file_handle, iconlist *icons);
+//   iconentry *get_nexticon(iconlist *icons, iconentry *last_entry);
+}
+
+/*
+ * Obtain an existing icon resource list
+ */
+int get_iconlist(libr_file *file_handle, iconlist *icons)
+{
+	if(icons == NULL)
+	{
+		/* Need to be able to return SOMETHING */
+		return false;
+	}
+	/* Obtain the icon resource list */
+	icons->buffer = libr_malloc(file_handle, ICON_SECTION, &(icons->size));
+	if(icons->buffer == NULL)
+		return false;
+	return true;
+}
+
+/*
+ * Get the next entry in an icon resource list
+ */
+iconentry *get_nexticon(iconlist *icons, iconentry *last_entry)
+{
+	size_t i;
+	
+	/* The icon list is needed both for the data buffer and for a call-specific iconentry instance */ 
+	if(icons == NULL)
+		return NULL;
+	/* If this is the first call (last_entry == NULL) then return the first entry */
+	if(last_entry == NULL)
+		icons->entry.offset = sizeof(uint32_t)+sizeof(UUID);
+	else
+		icons->entry.offset += icons->entry.entry_size;
+	/* Check to see if we've run out of entries */
+	if(icons->entry.offset >= icons->size)
+		return NULL;
+	i = icons->entry.offset;
+	memcpy(&(icons->entry.entry_size), &(icons->buffer[i]), sizeof(uint32_t));
+	i += sizeof(uint32_t);
+	icons->entry.type = (libr_icontype_t)icons->buffer[i];
+	i += sizeof(unsigned char);
+	switch(icons->entry.type)
+	{
+		case LIBR_SVG:
+			icons->entry.icon_size = 0;
+			icons->entry.name = &(icons->buffer[i]);
+			break;
+		case LIBR_PNG:
+			memcpy(&(icons->entry.icon_size), &(icons->buffer[i]), sizeof(uint32_t));
+			i += sizeof(uint32_t);
+			icons->entry.name = &(icons->buffer[i]);
+			break;
+		default:
+			/* Invalid entry type */
+			return NULL;
+	}
+	return &(icons->entry);
+}
+
+#endif // HAVE_ELFICON
+
 class KFileItem::KFileItemPrivate {
 	public:
 		TQString iconName;
@@ -630,6 +741,64 @@ TQPixmap KFileItem::pixmap( int _size, int _state ) const
   if (p.isNull())
       kdWarning() << "Pixmap not found for mimetype " << m_pMimeType->name() << endl;
 
+  if ( mime->name() == "application/x-executable" ) {
+	// FIXME
+	// Look for .desktop files for this executable
+	// before resorting to the embedded icon
+	// (look at how the minicli does it)
+#ifdef HAVE_ELFICON
+	// Check for an embedded icon
+	unsigned int icon_size;
+	libr_icon *icon = NULL;
+	libr_file *handle = NULL;
+	libr_access_t access = LIBR_READ;
+
+	if((handle = libr_open(const_cast<char*>(url.path().ascii()), access)) == NULL)
+	{
+		kdWarning() << "failed to open file" << url.path() << endl;
+		return p;
+	}
+	
+	icon_size = _size;
+	icon = libr_icon_geticon_bysize(handle, icon_size);
+	if(icon == NULL)
+	{
+		kdWarning() << "failed to obtain ELF icon: " << libr_errmsg() << endl;
+		libr_close(handle);
+		return p;
+	}
+
+	// See if the embedded icon name matches any icon file names already on the system
+	// If it does, use the system icon instead of the embedded one
+	int iconresnamefound = 0;
+	iconentry *entry = NULL;
+	iconlist icons;
+	if(!get_iconlist(handle, &icons))
+	{
+		// Failed to obtain a list of ELF icons
+	}
+	while((entry = get_nexticon(&icons, entry)) != NULL)
+	{
+		if (KGlobal::iconLoader()->iconPath(entry->name, _size, true) != "") {
+			iconresnamefound = 1;
+			p = DesktopIcon( entry->name, _size, _state );
+			break;
+		}
+	}
+
+	if (iconresnamefound == 0) {
+		// Extract the embedded icon
+		size_t icon_data_length;
+		char* icondata = libr_icon_malloc(icon, &icon_data_length);
+		p.loadFromData(static_cast<uchar*>(static_cast<void*>(icondata)), icon_data_length);	// EVIL CAST
+		free(icondata);
+		libr_icon_close(icon);
+	}
+
+	libr_close(handle);
+#endif // HAVE_ELFICON
+  }
+
   return p;
 }
 
-- 
cgit v1.2.3

