diff --git a/Clipboard.cabal b/Clipboard.cabal
--- a/Clipboard.cabal
+++ b/Clipboard.cabal
@@ -1,44 +1,51 @@
-Name:           Clipboard
-Version:        2.2.0.1
-Author:         Sævar Berg, Daniel Díaz
-Homepage:       http://dhelta.net/hprojects/Clipboard
-License:        BSD3
-License-file:   license
-Maintainer:     Daniel Diaz [danieldiaz `at` dhelta `dot` net]
-Category:       System
-Synopsis:       System clipboard interface.
-Bug-reports:    https://github.com/Daniel-Diaz/Clipboard/issues
-Description:    
-         /Clipboard/ is a library for easily interfacing with the system clipboard with additional unicode support.
-         Currently, only in a Windows system.
-         .
-         For example, if you type:
-         .
-         > setClipboardString "Hello, World!"
-         .
-         Then you have @\"Hello, World!\"@ available to be pasted where you want.
-         .
-         Now, if you type:
-         .
-         > modifyClipboardString reverse
-         .
-         You will have @\"!dlroW ,olleH\"@ in your clipboard. So:
-         .
-         >>> getClipboardString
-         "!dlroW ,olleH"
-         .
-         Changes from last version:
-         .
-         * New documentation with examples.
-Build-type:     Simple
-Cabal-version:  >= 1.6
-Extra-source-files: README.md
-
-Source-repository head
- type: git
- location: git://github.com/Daniel-Diaz/Clipboard.git
-
-Library
-  Exposed-modules: System.Clipboard
-  Build-depends:  base == 4.*
-                , Win32 == 2.2.*
+Name:         Clipboard
+Version:      2.3.2.2
+Author:       Sævar Berg (Windows), Matthew Bekkema (X11), Daniel Casanueva (Maintainer)
+License:      BSD3
+License-file: license
+Maintainer:   Daniel Casanueva (coding `at` danielcasanueva.eu)
+Category:     System
+Stability:    Stable
+Synopsis:     System clipboard interface.
+Bug-reports:  https://codeberg.org/daniel-casanueva/Clipboard/issues
+Description:    
+  /Clipboard/ is a library for easily interfacing with the system clipboard with additional unicode support.
+  Currently, only in a Windows or GNU/Linux (X11) system.
+  .
+  For example, if you type:
+  .
+  > $ setClipboardString "Hello, World!"
+  .
+  Then you have @\"Hello, World!\"@ available to be pasted wherever you want.
+  .
+  Now, if you type:
+  .
+  > $ modifyClipboardString reverse
+  .
+  You will have @\"!dlroW ,olleH\"@ in your clipboard. So:
+  .
+  > $ getClipboardString
+  > "!dlroW ,olleH"
+  .
+  The X11 version depends on the @X11@ package, so you will need the X11 development library
+  available on your system at compile time. You can install it with @apt install libxrandr-dev@
+  (or the equivalent on your system).
+Build-type: Simple
+Cabal-version: 1.18
+Extra-doc-files: README.md, changelog.md
+
+Library
+  Exposed-modules: System.Clipboard
+  Default-Language: Haskell2010
+  Default-extensions: CPP
+  if os(windows)
+    Build-depends:  base == 4.*
+                  , Win32 >= 2.2.0.0 && < 2.4
+    Other-modules:  System.Clipboard.Windows
+  else
+    Build-depends:  base == 4.*
+                  , X11 >= 1.6
+                  , utf8-string
+                  , unix
+                  , directory
+    Other-modules:  System.Clipboard.X11
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -4,4 +4,4 @@
 
 ## Limitations
 
-I'm so sorry, but this library **only works on Windows**. Contributions are welcome! :)
+I'm so sorry, but this library **only works on Windows and GNU/Linux (X11)**. Contributions are welcome! :)
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/System/Clipboard.hs b/System/Clipboard.hs
--- a/System/Clipboard.hs
+++ b/System/Clipboard.hs
@@ -1,81 +1,35 @@
--- | System clipboard interface with unicode support.
---
--- For more information, see "Graphics.Win32.GDI.Clip"
--- or documentation for /GetClipboardData/ on MSDN.
-module System.Clipboard
-    ( -- * Clipboard interface
-      setClipboardString
-    , getClipboardString 
-    , modifyClipboardString
-      -- * Clipboard format
-    , cF_UNICODETEXT
-    ) where
-
-import System.Win32.Mem 
-    (globalAlloc, globalLock, globalUnlock, copyMemory, gHND)
-import Graphics.Win32.GDI.Clip 
-    ( openClipboard, closeClipboard, emptyClipboard, 
-      getClipboardData, setClipboardData
-    , cF_TEXT, ClipboardFormat 
-    , isClipboardFormatAvailable)
-import Foreign.C
-   (withCAString, peekCAString, withCWString, peekCWString)
-import Foreign.Ptr
-    (castPtr, nullPtr)
-import Data.List
-    (genericLength)
-import Control.Exception 
-    (bracket_, bracket)
-import Data.Maybe
-    (isJust)
-
--- | Clipboard format of Unicode text.
-cF_UNICODETEXT :: ClipboardFormat
-cF_UNICODETEXT = 13
-
--- | Puts a string on the clipboard, using the 'cF_UNICODETEXT' clipboard format.
-setClipboardString :: String -> IO ()
-setClipboardString str =
-   withCWString str $ \cstring -> do
-   mem <- globalAlloc gHND strLen
-   bracket (globalLock mem) globalUnlock $ \mem' -> do
-       copyMemory mem' (castPtr cstring) strLen
-       bracket_ (openClipboard nullPtr) closeClipboard $ do
-           emptyClipboard
-           setClipboardData cF_UNICODETEXT mem'
-           return ()
- where
-   strLen = 2 * (genericLength str + 1)
-
--- | Gets the contents of the clipboard as a 'String'.
--- Returns 'Nothing' if the clipboard content is not
--- of type 'cF_TEXT' or 'cF_UNICODETEXT', i.e. it
--- doesn't contain /textual/ data.
-getClipboardString :: IO (Maybe String)
-getClipboardString =
-   bracket_ (openClipboard nullPtr) closeClipboard $ do
-     isUnicodeAvailable <- isClipboardFormatAvailable cF_UNICODETEXT
-     if isUnicodeAvailable
-       then do handle <- getClipboardData cF_UNICODETEXT
-               mem <- globalLock handle
-               str <- peekCWString (castPtr mem)
-               globalUnlock mem
-               return $ Just str
-       else do isAnsiAvailable <- isClipboardFormatAvailable cF_TEXT
-               if isAnsiAvailable
-                 then do handle <- getClipboardData cF_TEXT
-                         mem <- globalLock handle
-                         str <- peekCAString (castPtr mem)
-                         globalUnlock mem
-                         return $ Just str
-                 else return Nothing
-
--- | Modifies the clipboard content.
--- If the clipboard has /textual/ data, this function modifies its content,
--- and return 'True'. Otherwise, it does nothing and return 'False'.
-modifyClipboardString :: (String -> String) -> IO Bool
-modifyClipboardString f = do
- s <- getClipboardString
- case s of
-   Nothing -> return False
+
+{-# LANGUAGE CPP #-}
+
+-- | System Clipboard Interface. It should work on both Windows and Unix (X11).
+module System.Clipboard
+    ( -- * Clipboard interface
+      setClipboardString
+    , getClipboardString
+    , modifyClipboardString
+    ) where
+
+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
+import qualified System.Clipboard.Windows as OS
+#else
+import qualified System.Clipboard.X11 as OS
+#endif
+
+-- | Write a string to the clipboard.
+setClipboardString :: String -> IO ()
+setClipboardString = OS.setClipboardString
+
+-- | Get the content of the clipboard as a 'String'.
+--   It returns 'Nothing' if the clipboard doesn't contain /textual/ data.
+getClipboardString :: IO (Maybe String)
+getClipboardString = OS.getClipboardString
+
+-- | Modify the clipboard content.
+--   If the clipboard has /textual/ data, this function modifies its content
+--   and returns 'True'. Otherwise, it does nothing and returns 'False'.
+modifyClipboardString :: (String -> String) -> IO Bool
+modifyClipboardString f = do
+ s <- getClipboardString
+ case s of
+   Nothing -> return False
    Just sc -> setClipboardString (f sc) >> return True
diff --git a/System/Clipboard/Windows.hs b/System/Clipboard/Windows.hs
new file mode 100644
--- /dev/null
+++ b/System/Clipboard/Windows.hs
@@ -0,0 +1,61 @@
+-- | System clipboard interface with unicode support.
+--
+-- For more information, see "Graphics.Win32.GDI.Clip"
+-- or documentation for /GetClipboardData/ on MSDN.
+module System.Clipboard.Windows
+    ( setClipboardString
+    , getClipboardString
+    ) where
+
+import System.Win32.Mem
+    (globalAlloc, globalLock, globalUnlock, copyMemory, gHND)
+import Graphics.Win32.GDI.Clip
+    ( openClipboard, closeClipboard, emptyClipboard,
+      getClipboardData, setClipboardData
+    , cF_TEXT, ClipboardFormat
+    , isClipboardFormatAvailable)
+import Foreign.C
+   (withCAString, peekCAString, withCWString, peekCWString)
+import Foreign.Ptr
+    (castPtr, nullPtr)
+import Data.List
+    (genericLength)
+import Control.Exception
+    (bracket_, bracket)
+import Data.Maybe
+    (isJust)
+
+cF_UNICODETEXT :: ClipboardFormat
+cF_UNICODETEXT = 13
+
+setClipboardString :: String -> IO ()
+setClipboardString str =
+   withCWString str $ \cstring -> do
+   mem <- globalAlloc gHND strLen
+   bracket (globalLock mem) globalUnlock $ \mem' -> do
+       copyMemory mem' (castPtr cstring) strLen
+       bracket_ (openClipboard nullPtr) closeClipboard $ do
+           emptyClipboard
+           setClipboardData cF_UNICODETEXT mem'
+           return ()
+ where
+   strLen = 2 * (genericLength str + 1)
+
+getClipboardString :: IO (Maybe String)
+getClipboardString =
+   bracket_ (openClipboard nullPtr) closeClipboard $ do
+     isUnicodeAvailable <- isClipboardFormatAvailable cF_UNICODETEXT
+     if isUnicodeAvailable
+       then do handle <- getClipboardData cF_UNICODETEXT
+               mem <- globalLock handle
+               str <- peekCWString (castPtr mem)
+               globalUnlock mem
+               return $ Just str
+       else do isAnsiAvailable <- isClipboardFormatAvailable cF_TEXT
+               if isAnsiAvailable
+                 then do handle <- getClipboardData cF_TEXT
+                         mem <- globalLock handle
+                         str <- peekCAString (castPtr mem)
+                         globalUnlock mem
+                         return $ Just str
+                 else return Nothing
diff --git a/System/Clipboard/X11.hs b/System/Clipboard/X11.hs
new file mode 100644
--- /dev/null
+++ b/System/Clipboard/X11.hs
@@ -0,0 +1,118 @@
+
+{-# LANGUAGE RecordWildCards, CPP #-}
+
+module System.Clipboard.X11
+  ( getClipboardString
+  , setClipboardString
+  ) where
+
+import Graphics.X11.Xlib
+import Graphics.X11.Xlib.Extras
+import System.Posix.Process     (forkProcess)
+
+import Codec.Binary.UTF8.String (decode, encode)
+import Control.Monad
+import Data.Maybe
+#if !MIN_VERSION_X11(1,8,0)
+import Foreign                  (peekByteOff)
+#endif
+import Foreign.C.Types          (CChar, CUChar)
+import Foreign.Marshal.Array    (withArrayLen)
+import System.Directory         (setCurrentDirectory)
+import System.IO                (hClose, stderr, stdin, stdout)
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative ((<$>))
+#endif
+
+getClipboardString :: IO (Maybe String)
+getClipboardString = do
+    (display, window, clipboards) <- initialSetup
+    inp <- internAtom display "clipboard_get" False
+    target <- internAtom display "UTF8_STRING" True
+    xConvertSelection display (head clipboards) target inp window currentTime
+    ret <- clipboardInputWait display window inp
+    cleanup display window
+    return ret
+
+clipboardInputWait :: Display -> Window -> Atom -> IO (Maybe String)
+clipboardInputWait display window inp = do
+    ev <- getNextEvent display
+    if ev_event_type ev == selectionNotify
+        then fmap charsToString <$> getWindowProperty8 display inp window
+        else clipboardInputWait display window inp
+
+charsToString :: [CChar] -> String
+charsToString = decode . map fromIntegral
+
+setClipboardString :: String -> IO ()
+setClipboardString str = do
+    (display, window, clipboards) <- initialSetup
+    mapM_ (\atom -> xSetSelectionOwner display atom window currentTime) clipboards
+    void $ forkProcess $ do
+        hClose stdin
+        hClose stdout
+        hClose stderr
+        setCurrentDirectory "/"
+        advertiseSelection display clipboards (stringToChars str)
+        cleanup display window
+
+advertiseSelection :: Display -> [Atom] -> [CUChar] -> IO ()
+advertiseSelection display clipboards' str = allocaXEvent (go clipboards')
+  where
+    go [] _ = return ()
+    go clipboards evPtr = do
+      nextEvent display evPtr
+      ev <- getEvent evPtr
+      case ev of
+          SelectionRequest {..} -> do
+              target' <- getAtomName display ev_target
+              res <- handleOutput display ev_requestor ev_property target' str
+              sendSelectionNotify display ev_requestor ev_selection ev_target res ev_time
+              go clipboards evPtr
+
+#if MIN_VERSION_X11(1,8,0)
+          SelectionClear {..} -> go (filter (/= ev_selection) clipboards) evPtr
+#else
+          _ | ev_event_type ev == selectionClear -> do
+              target <- peekByteOff evPtr 40 :: IO Atom
+              go (filter (/= target) clipboards) evPtr
+#endif
+          _ -> go clipboards evPtr
+
+handleOutput :: Display -> Window -> Atom -> Maybe String -> [CUChar] -> IO Atom
+handleOutput display req prop (Just "UTF8_STRING") str = do
+    prop' <- getAtomName display prop
+    if isNothing prop' then handleOutput display req prop Nothing str else do
+        target <- internAtom display "UTF8_STRING" True
+        void $ withArrayLen str $ \len str' ->
+            xChangeProperty display req prop target 8 propModeReplace str'
+                            (fromIntegral len)
+        return prop
+handleOutput _ _ _ _ _ = return none
+
+sendSelectionNotify :: Display -> Window -> Atom -> Atom -> Atom -> Time -> IO ()
+sendSelectionNotify display req sel target prop time = allocaXEvent $ \ev -> do
+    setEventType ev selectionNotify
+    setSelectionNotify ev req sel target prop time
+    sendEvent display req False 0 ev
+
+stringToChars :: String -> [CUChar]
+stringToChars = map fromIntegral . encode
+
+initialSetup :: IO (Display, Window, [Atom])
+initialSetup = do
+    display <- openDisplay ""
+    window <- createSimpleWindow display (defaultRootWindow display)
+                                 0 0 1 1 0 0 0
+    clipboards <- internAtom display "CLIPBOARD" False
+    return (display, window, [clipboards, pRIMARY])
+
+cleanup :: Display -> Window -> IO ()
+cleanup display window = do
+    destroyWindow display window
+    closeDisplay display
+
+getNextEvent :: Display -> IO Event
+getNextEvent display = allocaXEvent $ \ev -> do
+    nextEvent display ev
+    getEvent ev
diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,5 @@
+## 2.3.2.2
+* Metadata update.
+
+## 2.3.2.1
+* Metadata update.
diff --git a/license b/license
--- a/license
+++ b/license
@@ -1,4 +1,4 @@
-Copyright (c)2011, Daniel Díaz
+Copyright (c)2015, Daniel Díaz
 
 All rights reserved.
 
